Media items, playlists, and tracks
This article shows you how to use the MediaSource class, which provides a common way to reference and play back media from different sources such as local or remote files and exposes a common model for accessing media data, regardless of the underlying media format. The MediaPlaybackItem class extends the functionality of MediaSource, allowing you to manage and select from multiple audio, video, and metadata tracks contained in a media item. MediaPlaybackList allows you to create playback lists from one or more media playback items.
Create and play a MediaSource
Create a new instance of MediaSource by calling one of the factory methods exposed by the class:
- CreateFromAdaptiveMediaSource
- CreateFromIMediaSource
- CreateFromMediaStreamSource
- CreateFromMseStreamSource
- CreateFromStorageFile
- CreateFromStream
- CreateFromStreamReference
- CreateFromUri
- CreateFromDownloadOperation
After creating a MediaSource you can play it with a MediaPlayer by setting the Source property. Starting with Windows 10, version 1607, you can assign a MediaPlayer to a MediaPlayerElement by calling SetMediaPlayer in order to render the media player content in a XAML page. This is the preferred method over using MediaElement. For more information on using MediaPlayer, see Play audio and video with MediaPlayer.
The following example shows how to play back a user-selected media file in a MediaPlayer using MediaSource.
You will need to include the Windows.Media.Core and Windows.Media.Playback namespaces in order to complete this scenario.
using Windows.Media.Core;
using Windows.Media.Playback;
Declare a variable of type MediaSource. For the examples in this article, the media source is declared as a class member so that it can be accessed from multiple locations.
MediaSource _mediaSource;
Declare a variable to store the MediaPlayer object and, if you want to render the media content in XAML, add a MediaPlayerElement control to your page.
MediaPlayer _mediaPlayer;
<MediaPlayerElement x:Name="mediaPlayerElement"/>
To allow the user to pick a media file to play, use a FileOpenPicker. With the StorageFile object returned from the picker's PickSingleFileAsync method, initialize a new MediaObject by calling MediaSource.CreateFromStorageFile. Finally, set the media source as the playback source for the MediaElement by calling the SetPlaybackSource method.
//Create a new picker
var filePicker = new Windows.Storage.Pickers.FileOpenPicker();
//make a collection of all video types you want to support (for testing we are adding just 3).
string[] fileTypes = new string[] {".wmv", ".mp4", ".mkv"};
//Add your fileTypes to the FileTypeFilter list of filePicker.
foreach (string fileType in fileTypes)
{
filePicker.FileTypeFilter.Add(fileType);
}
//Set picker start location to the video library
filePicker.SuggestedStartLocation = PickerLocationId.VideosLibrary;
//Retrieve file from picker
StorageFile file = await filePicker.PickSingleFileAsync();
if (!(file is null))
{
_mediaSource = MediaSource.CreateFromStorageFile(file);
_mediaPlayer = new MediaPlayer();
_mediaPlayer.Source = _mediaSource;
mediaPlayerElement.SetMediaPlayer(_mediaPlayer);
}
By default, the MediaPlayer does not begin playing automatically when the media source is set. You can manually begin playback by calling Play.
_mediaPlayer.Play();
You can also set the AutoPlay property of the MediaPlayer to true to tell the player to begin playing as soon as the media source is set.
_mediaPlayer.AutoPlay = true;
Create a MediaSource from a DownloadOperation
Starting with Windows, version 1803, you can create a MediaSource object from a DownloadOperation.
StorageFile destinationFile = await KnownFolders.VideosLibrary.CreateFileAsync("file.mp4", CreationCollisionOption.GenerateUniqueName);
var downloader = new BackgroundDownloader();
var downloadOperation = downloader.CreateDownload(new Uri("http://server.com/file.mp4"), destinationFile);
MediaSource mediaSource =
MediaSource.CreateFromDownloadOperation(downloadOperation);
Note that while you can create a MediaSource from a download without starting it or setting its IsRandomAccessRequired property to true, you must do both of these things before attempting to attach the MediaSource to a MediaPlayer or MediaPlayerElement for playback.
downloadOperation.IsRandomAccessRequired = true;
var startAsyncTask = downloadOperation.StartAsync().AsTask();
mediaPlayerElement.Source = mediaSource;
Handle multiple audio, video, and metadata tracks with MediaPlaybackItem
Using a MediaSource for playback is convenient because it provides a common way to playback media from different kinds of sources, but more advanced behavior can be accessed by creating a MediaPlaybackItem from the MediaSource. This includes the ability to access and manage multiple audio, video, and data tracks for a media item.
Declare a variable to store your MediaPlaybackItem.
MediaPlaybackItem _mediaPlaybackItem;
Create a MediaPlaybackItem by calling the constructor and passing in an initialized MediaSource object.
If your app supports multiple audio, video, or data tracks in a media playback item, register event handlers for the AudioTracksChanged, VideoTracksChanged, or TimedMetadataTracksChanged events.
Finally, set the playback source of the MediaElement or MediaPlayer to your MediaPlaybackItem.
_mediaSource = MediaSource.CreateFromStorageFile(file);
_mediaPlaybackItem = new MediaPlaybackItem(_mediaSource);
_mediaPlaybackItem.AudioTracksChanged += PlaybackItem_AudioTracksChanged;
_mediaPlaybackItem.VideoTracksChanged += MediaPlaybackItem_VideoTracksChanged;
_mediaPlaybackItem.TimedMetadataTracksChanged += MediaPlaybackItem_TimedMetadataTracksChanged;
_mediaPlayer = new MediaPlayer();
_mediaPlayer.Source = _mediaPlaybackItem;
mediaPlayerElement.SetMediaPlayer(_mediaPlayer);
Note
A MediaSource can only be associated with a single MediaPlaybackItem. After creating a MediaPlaybackItem from a source, attempting to create another playback item from the same source will result in an error. Also, after creating a MediaPlaybackItem from a media source, you can't set the MediaSource object directly as the source for a MediaPlayer but should instead use the MediaPlaybackItem.
The VideoTracksChanged event is raised after a MediaPlaybackItem containing multiple video tracks is assigned as a playback source, and can be raised again if the list of video tracks changes for the item changes. The handler for this event gives you the opportunity to update your UI to allow the user to switch between available tracks. This example uses a ComboBox to display the available video tracks.
<ComboBox x:Name="videoTracksComboBox" SelectionChanged="videoTracksComboBox_SelectionChanged"/>
In the VideoTracksChanged handler, loop through all of the tracks in the playback item's VideoTracks list. For each track, a new ComboBoxItem is created. If the track does not already have a label, a label is generated from the track index. The Tag property of the combo box item is set to the track index so that it can be identified later. Finally, the item is added to the combo box. Note that these operations are performed within a CoreDispatcher.RunAsync call because all UI changes must be made on the UI thread and this event is raised on a different thread.
private async void MediaPlaybackItem_VideoTracksChanged(MediaPlaybackItem sender, IVectorChangedEventArgs args)
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
videoTracksComboBox.Items.Clear();
for (int index = 0; index < sender.VideoTracks.Count; index++)
{
var videoTrack = sender.VideoTracks[index];
ComboBoxItem item = new ComboBoxItem();
item.Content = String.IsNullOrEmpty(videoTrack.Label) ? $"Track {index}" : videoTrack.Label;
item.Tag = index;
videoTracksComboBox.Items.Add(item);
}
});
}
In the SelectionChanged handler for the combo box, the track index is retrieved from the selected item's Tag property. Setting the SelectedIndex property of the media playback item's VideoTracks list causes the MediaElement or MediaPlayer to switch the active video track to the specified index.
private void videoTracksComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
int trackIndex = (int)((ComboBoxItem)((ComboBox)sender).SelectedItem).Tag;
_mediaPlaybackItem.VideoTracks.SelectedIndex = trackIndex;
}
Managing media items with multiple audio tracks works exactly the same as with video tracks. Handle the AudioTracksChanged to update your UI with the audio tracks found in the playback item's AudioTracks list. When the user selects an audio track, set the SelectedIndex property of the AudioTracks list to cause the MediaElement or MediaPlayer to switch the active audio track to the specified index.
<ComboBox x:Name="audioTracksComboBox" SelectionChanged="audioTracksComboBox_SelectionChanged"/>
private async void PlaybackItem_AudioTracksChanged(MediaPlaybackItem sender, IVectorChangedEventArgs args)
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
audioTracksComboBox.Items.Clear();
for (int index = 0; index < sender.AudioTracks.Count; index++)
{
var audioTrack = sender.AudioTracks[index];
ComboBoxItem item = new ComboBoxItem();
item.Content = String.IsNullOrEmpty(audioTrack.Label) ? $"Track {index}" : audioTrack.Label;
item.Tag = index;
videoTracksComboBox.Items.Add(item);
}
});
}
private void audioTracksComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
int trackIndex = (int)((ComboBoxItem)((ComboBox)sender).SelectedItem).Tag;
_mediaPlaybackItem.AudioTracks.SelectedIndex = trackIndex;
}
In addition to audio and video, a MediaPlaybackItem object may contain zero or more TimedMetadataTrack objects. A timed metadata track can contain subtitle or caption text, or it may contain custom data that is proprietary to your app. A timed metadata track contains a list of cues represented by objects that inherit from IMediaCue, such as a DataCue or a TimedTextCue. Each cue has a start time and a duration that determines when the cue is activated and for how long.
Similar to audio tracks and video tracks, the timed metadata tracks for a media item can be discovered by handling the TimedMetadataTracksChanged event of a MediaPlaybackItem. With timed metadata tracks, however, the user may want to enable more than one metadata track at a time. Also, depending on your app scenario, you may want to enable or disable metadata tracks automatically, without user intervention. For illustration purposes, this example adds a ToggleButton for each metadata track in a media item to allow the user to enable and disable the track. The Tag property of each button is set to the index of the associated metadata track so that it can be identified when the button is toggled.
<StackPanel x:Name="MetadataButtonPanel" Orientation="Horizontal"/>
private async void MediaPlaybackItem_TimedMetadataTracksChanged(MediaPlaybackItem sender, IVectorChangedEventArgs args)
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
for (int index = 0; index < sender.TimedMetadataTracks.Count; index++)
{
var timedMetadataTrack = sender.TimedMetadataTracks[index];
ToggleButton toggle = new ToggleButton()
{
Content = String.IsNullOrEmpty(timedMetadataTrack.Label) ? $"Track {index}" : timedMetadataTrack.Label,
Tag = (uint)index
};
toggle.Checked += Toggle_Checked;
toggle.Unchecked += Toggle_Unchecked;
MetadataButtonPanel.Children.Add(toggle);
}
});
}
Because more than one metadata track can be active at a time, you don't simply set the active index for the metadata track list. Instead, call the MediaPlaybackItem object's SetPresentationMode method, passing in the index of the track you want to toggle, and then providing a value from the TimedMetadataTrackPresentationMode enumeration. The presentation mode you choose depends on the implementation of your app. In this example, the metadata track is set to PlatformPresented when enabled. For text-based tracks, this means that the system will automatically display the text cues in the track. When the toggle button is toggled off, the presentation mode is set to Disabled, which means that no text is displayed and no cue events are raised. Cue events are discussed later in this article.
private void Toggle_Checked(object sender, RoutedEventArgs e) =>
_mediaPlaybackItem.TimedMetadataTracks.SetPresentationMode((uint)((ToggleButton)sender).Tag,
TimedMetadataTrackPresentationMode.PlatformPresented);
private void Toggle_Unchecked(object sender, RoutedEventArgs e) =>
_mediaPlaybackItem.TimedMetadataTracks.SetPresentationMode((uint)((ToggleButton)sender).Tag,
TimedMetadataTrackPresentationMode.Disabled);
As you are processing the metadata tracks, you can access the set of cues within the track by accessing the Cues or ActiveCues properties. You can do this to update your UI to show the cue locations for a media item.
Handle unsupported codecs and unknown errors when opening media items
Starting with Windows 10, version 1607, you can check whether the codec required to playback a media item is supported or partially supported on the device on which your app is running. In the event handler for the MediaPlaybackItem tracks-changed events, such as AudioTracksChanged, first check to see if the track change is an insertion of a new track. If so, you can get a reference to the track being inserted by using the index passed in the IVectorChangedEventArgs.Index parameter with the appropriate track collection of the MediaPlaybackItem parameter, such as the AudioTracks collection.
Once you have a reference to the inserted track, check the DecoderStatus of the track's SupportInfo property. If the value is FullySupported, then the appropriate codec needed to play back the track is present on the device. If the value is Degraded, then the track can be played by the system, but the playback will be degraded in some way. For example, a 5.1 audio track may be played back as 2-channel stereo instead. If this is the case, you may want to update your UI to alert the user of the degradation. If the value is UnsupportedSubtype or UnsupportedEncoderProperties, then the track can't be played back at all with the current codecs on the device. You may wish to alert the user and skip playback of the item or implement UI to allow the user to download the correct codec. The track's GetEncodingProperties method can be used to determine the required codec for playback.
Finally, you can register for the track's OpenFailed event, which will be raised if the track is supported on the device but failed to open due to an unknown error in the pipeline.
private async void SnippetAudioTracksChanged_CodecCheck(MediaPlaybackItem sender, IVectorChangedEventArgs args)
{
if (args.CollectionChange == CollectionChange.ItemInserted)
{
var insertedTrack = sender.AudioTracks[(int)args.Index];
var decoderStatus = insertedTrack.SupportInfo.DecoderStatus;
if (decoderStatus != MediaDecoderStatus.FullySupported)
{
if (decoderStatus == MediaDecoderStatus.Degraded)
{
ShowMessageToUser($"Track {insertedTrack.Name} can play but playback will be degraded. {insertedTrack.SupportInfo.DegradationReason}");
}
else
{
// status is MediaDecoderStatus.UnsupportedSubtype or MediaDecoderStatus.UnsupportedEncoderProperties
ShowMessageToUser($"Track {insertedTrack.Name} uses an unsupported media format.");
}
Windows.Media.MediaProperties.AudioEncodingProperties props = insertedTrack.GetEncodingProperties();
await HelpUserInstallCodec(props);
}
else
{
insertedTrack.OpenFailed += InsertedTrack_OpenFailed;
}
}
}
In the OpenFailed event handler, you can check to see if the MediaSource status is unknown, and if so, you can programmatically select a different track to play, allow the user to choose a different track, or abandon playback.
private async void InsertedTrack_OpenFailed(AudioTrack sender, AudioTrackOpenFailedEventArgs args)
{
LogError(args.ExtendedError.HResult);
if (sender.SupportInfo.MediaSourceStatus == MediaSourceStatus.Unknown)
{
await SelectAnotherTrackOrSkipPlayback(sender.PlaybackItem);
}
}
Set display properties used by the System Media Transport Controls
Starting with Windows 10, version 1607, media played in a MediaPlayer is automatically integrated into the System Media Transport Controls (SMTC) by default. You can specify the metadata that will be displayed by the SMTC by updating the display properties for a MediaPlaybackItem. Get an object representing the display properties for an item by calling GetDisplayProperties. Set whether the playback item is music or video by setting the Type property. Then, set the properties of the object's VideoProperties or MusicProperties. Call ApplyDisplayProperties to update the item's properties to the values you provided. Typically, an app will retrieve the display values dynamically from a web service, but the following example illustrates this process with hardcoded values.
MediaItemDisplayProperties props = mediaPlaybackItem.GetDisplayProperties();
props.Type = Windows.Media.MediaPlaybackType.Video;
props.VideoProperties.Title = "Video title";
props.VideoProperties.Subtitle = "Video subtitle";
props.VideoProperties.Genres.Add("Documentary");
mediaPlaybackItem.ApplyDisplayProperties(props);
props = mediaPlaybackItem.GetDisplayProperties();
props.Type = Windows.Media.MediaPlaybackType.Music;
props.MusicProperties.Title = "Song title";
props.MusicProperties.Artist = "Song artist";
props.MusicProperties.Genres.Add("Polka");
mediaPlaybackItem.ApplyDisplayProperties(props);
Add external timed text with TimedTextSource
For some scenarios, you may have external files that contains timed text associated with a media item, such as separate files that contain subtitles for different locales. Use the TimedTextSource class to load in external timed text files from a stream or URI.
This example uses a Dictionary collection to store a list of the timed text sources for the media item using the source URI and the TimedTextSource object as the key/value pair in order to identify the tracks after they have been resolved.
Dictionary<TimedTextSource, Uri> timedTextSourceMap;
Create a new TimedTextSource for each external timed text file by calling CreateFromUri. Add an entry to the Dictionary for the timed text source. Add a handler for the TimedTextSource.Resolved event to handle if the item failed to load or to set additional properties after the item was loaded successfully.
Register all of your TimedTextSource objects with the MediaSource by adding them to the ExternalTimedTextSources collection. Note that external timed text sources are added to directly the MediaSource and not the MediaPlaybackItem created from the source. To update your UI to reflect the external text tracks, register and handle the TimedMetadataTracksChanged event as described previously in this article.
// Create the TimedTextSource and add entry to URI map
var timedTextSourceUri_En = new Uri("http://contoso.com/MyClipTimedText_en.srt");
var timedTextSource_En = TimedTextSource.CreateFromUri(timedTextSourceUri_En);
timedTextSourceMap[timedTextSource_En] = timedTextSourceUri_En;
timedTextSource_En.Resolved += TimedTextSource_Resolved;
var timedTextSourceUri_Pt = new Uri("http://contoso.com/MyClipTimedText_pt.srt");
var timedTextSource_Pt = TimedTextSource.CreateFromUri(timedTextSourceUri_Pt);
timedTextSourceMap[timedTextSource_Pt] = timedTextSourceUri_Pt;
timedTextSource_Pt.Resolved += TimedTextSource_Resolved;
// Add the TimedTextSource to the MediaSource
_mediaSource.ExternalTimedTextSources.Add(timedTextSource_En);
_mediaSource.ExternalTimedTextSources.Add(timedTextSource_Pt);
_mediaPlaybackItem = new MediaPlaybackItem(_mediaSource);
_mediaPlaybackItem.TimedMetadataTracksChanged += MediaPlaybackItem_TimedMetadataTracksChanged;
_mediaPlayer = new MediaPlayer();
_mediaPlayer.Source = _mediaPlaybackItem;
mediaPlayerElement.SetMediaPlayer(_mediaPlayer);
In the handler for the TimedTextSource.Resolved event, check the Error property of the TimedTextSourceResolveResultEventArgs passed into the handler to determine if an error occurred while trying to load the timed text data. If the item was resolved successfully, you can use this handler to update additional properties of the resolved track. This example adds a label for each track based on the URI previously stored in the Dictionary.
private void TimedTextSource_Resolved(TimedTextSource sender, TimedTextSourceResolveResultEventArgs args)
{
var timedTextSourceUri = timedTextSourceMap[sender];
if (!(args.Error is null))
{
// Show that there was an error in your UI
ShowMessageToUser($"There was an error resolving track: {timedTextSourceUri}");
return;
}
// Add a label for each resolved track
var timedTextSourceUriString = timedTextSourceUri.AbsoluteUri;
if (timedTextSourceUriString.Contains("_en"))
{
args.Tracks[0].Label = "English";
}
else if (timedTextSourceUriString.Contains("_pt"))
{
args.Tracks[0].Label = "Portuguese";
}
}
For a list of the timed text formats that are supported on Windows, see Supported codecs.
Add additional metadata tracks
You can dynamically create custom metadata tracks in code and associate them with a media source. The tracks you create can contain subtitle or caption text, or they can contain your proprietary app data.
Create a new TimedMetadataTrack by calling the constructor and specifying an ID, the language identifier, and a value from the TimedMetadataKind enumeration. Register handlers for the CueEntered and CueExited events. These events are raised when the start time for a cue has been reached and when the duration for a cue has expired, respectively.
Create a new cue object, appropriate for the type of metadata track you created, and set the ID, start time, and duration for the track. This example creates a data track, so a set of DataCue objects are generated and a buffer containing app-specific data is provided for each cue. To register the new track, add it to the ExternalTimedMetadataTracks collection of the MediaSource object.
Starting with Windows 10, version 1703, the DataCue.Properties property exposes a PropertySet that you can use to store custom properties in key/data pairs that can be retrieved in the CueEntered and CueExited events.
TimedMetadataTrack metadataTrack = new TimedMetadataTrack("ID_0", "en-us", TimedMetadataKind.Data);
metadataTrack.Label = "Custom data track";
metadataTrack.CueEntered += MetadataTrack_DataCueEntered;
metadataTrack.CueExited += MetadataTrack_CueExited;
// Example cue data
string data = "Cue data";
byte[] bytes = new byte[data.Length * sizeof(char)];
System.Buffer.BlockCopy(data.ToCharArray(), 0, bytes, 0, bytes.Length);
Windows.Storage.Streams.IBuffer buffer = bytes.AsBuffer();
for (int i = 0; i < 10; i++)
{
DataCue cue = new DataCue();
cue.Id = "ID_" + i;
cue.Data = buffer;
cue.Properties["AdUrl"] = "http://contoso.com/ads/123";
cue.StartTime = TimeSpan.FromSeconds(3 + i * 3);
cue.Duration = TimeSpan.FromSeconds(2);
metadataTrack.AddCue(cue);
}
_mediaSource.ExternalTimedMetadataTracks.Add(metadataTrack);
The CueEntered event is raised when a cue's start time has been reached as long as the associated track has a presentation mode of ApplicationPresented, Hidden, or PlatformPresented. Cue events are not raised for metadata tracks while the presentation mode for the track is Disabled. This example simply outputs the custom data associated with the cue to the debug window.
private void MetadataTrack_DataCueEntered(TimedMetadataTrack sender, MediaCueEventArgs args)
{
DataCue cue = (DataCue)args.Cue;
string data = System.Text.Encoding.Unicode.GetString(cue.Data.ToArray());
System.Diagnostics.Debug.WriteLine("Cue entered: " + data);
System.Diagnostics.Debug.WriteLine("Custom prop value: " + cue.Properties["AdUrl"]);
}
This example adds a custom text track by specifying TimedMetadataKind.Caption when creating the track and using TimedTextCue objects to add cues to the track.
TimedMetadataTrack metadataTrack = new TimedMetadataTrack("TrackID_0", "en-us", TimedMetadataKind.Caption);
metadataTrack.Label = "Custom text track";
metadataTrack.CueEntered += MetadataTrack_TextCueEntered;
for (int i = 0; i < 10; i++)
{
TimedTextCue cue = new TimedTextCue()
{
Id = "TextCueID_" + i,
StartTime = TimeSpan.FromSeconds(i * 3),
Duration = TimeSpan.FromSeconds(2)
};
cue.Lines.Add(new TimedTextLine() { Text = "This is a custom timed text cue." });
metadataTrack.AddCue(cue);
}
_mediaSource.ExternalTimedMetadataTracks.Add(metadataTrack);
private void MetadataTrack_TextCueEntered(TimedMetadataTrack sender, MediaCueEventArgs args)
{
TimedTextCue cue = (TimedTextCue)args.Cue;
System.Diagnostics.Debug.WriteLine("Cue entered: " + cue.Id + " " + cue.Lines[0].Text);
}
Play a list of media items with MediaPlaybackList
The MediaPlaybackList allows you to create a playlist of media items, which are represented by MediaPlaybackItem objects.
Note Items in a MediaPlaybackList are rendered using gapless playback. The system will use provided metadata in MP3 or AAC encoded files to determine the delay or padding compensation needed for gapless playback. If the MP3 or AAC encoded files don't provide this metadata, then the system determines the delay or padding heuristically. For lossless formats, such as PCM, FLAC, or ALAC, the system takes no action because these encoders don't introduce delay or padding.
To get started, declare a variable to store your MediaPlaybackList.
MediaPlaybackList _mediaPlaybackList;
Create a MediaPlaybackItem for each media item you want to add to your list using the same procedure described previously in this article. Initialize your MediaPlaybackList object and add the media playback items to it. Register a handler for the CurrentItemChanged event. This event allows you to update your UI to reflect the currently playing media item. You can also register for the ItemOpened event, which is raised when an item in the list is successfully opened, and the ItemFailed event, which is raised when an item in the list can't be opened.
Starting with Windows 10, version 1703, you can specify the maximum number of MediaPlaybackItem objects in the MediaPlaybackList that the system will keep open after they have been played by setting the MaxPlayedItemsToKeepOpen property. When a MediaPlaybackItem is kept open, playback of the item can start instantaneously when the user switches to that item because the item doesn't need to be reloaded. But keeping items open also increases the memory consumption of your app, so you should consider the balance between responsiveness and memory usage when setting this value.
To enable playback of your list, set the playback source of the MediaPlayer to your MediaPlaybackList.
_mediaPlaybackList = new MediaPlaybackList();
var files = await filePicker.PickMultipleFilesAsync();
foreach (var file in files)
{
var mediaPlaybackItem = new MediaPlaybackItem(MediaSource.CreateFromStorageFile(file));
_mediaPlaybackList.Items.Add(mediaPlaybackItem);
}
_mediaPlaybackList.CurrentItemChanged += MediaPlaybackList_CurrentItemChanged;
_mediaPlaybackList.ItemOpened += MediaPlaybackList_ItemOpened;
_mediaPlaybackList.ItemFailed += MediaPlaybackList_ItemFailed;
_mediaPlaybackList.MaxPlayedItemsToKeepOpen = 3;
_mediaPlayer = new MediaPlayer();
_mediaPlayer.Source = _mediaPlaybackList;
mediaPlayerElement.SetMediaPlayer(_mediaPlayer);
In the CurrentItemChanged event handler, update your UI to reflect the currently playing item, which can be retrieved using the NewItem property of the CurrentMediaPlaybackItemChangedEventArgs object passed into the event. Remember that if you update the UI from this event, you should do so within a call to CoreDispatcher.RunAsync so that the updates are made on the UI thread.
Starting with Windows 10, version 1703, you can check the CurrentMediaPlaybackItemChangedEventArgs.Reason property to get a value that indicates the reason that the item changed, such as the app switching items programmatically, the previously playing item reaching its end, or an error occurring.
private void MediaPlaybackList_CurrentItemChanged(MediaPlaybackList sender, CurrentMediaPlaybackItemChangedEventArgs args) =>
LogTelemetryData($"CurrentItemChanged reason: {args.Reason.ToString()}");
Call MovePrevious or MoveNext to cause the media player to play the previous or next item in your MediaPlaybackList.
private void prevButton_Click(object sender, RoutedEventArgs e) => _mediaPlaybackList.MovePrevious();
private void nextButton_Click(object sender, RoutedEventArgs e) => _mediaPlaybackList.MoveNext();
Set the ShuffleEnabled property to specify whether the media player should play the items in your list in random order.
private async void shuffleButton_Click(object sender, RoutedEventArgs e)
{
_mediaPlaybackList.ShuffleEnabled = !_mediaPlaybackList.ShuffleEnabled;
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
shuffleButton.FontWeight =
_mediaPlaybackList.ShuffleEnabled ? Windows.UI.Text.FontWeights.Bold : Windows.UI.Text.FontWeights.Light;
});
}
Set the AutoRepeatEnabled property to specify whether the media player should loop playback of your list.
private async void autoRepeatButton_Click(object sender, RoutedEventArgs e)
{
_mediaPlaybackList.AutoRepeatEnabled = !_mediaPlaybackList.AutoRepeatEnabled;
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
autoRepeatButton.FontWeight =
_mediaPlaybackList.AutoRepeatEnabled ? Windows.UI.Text.FontWeights.Bold : Windows.UI.Text.FontWeights.Light;
});
}
Handle the failure of media items in a playback list
The ItemFailed event is raised when an item in the list fails to open. The ErrorCode property of the MediaPlaybackItemError object passed into the handler enumerates the specific cause of the failure when possible, including network errors, decoding errors, or encryption errors.
private void MediaPlaybackList_ItemFailed(MediaPlaybackList sender, MediaPlaybackItemFailedEventArgs args)
{
LogError(args.Error.ErrorCode.ToString());
LogError(args.Error.ExtendedError.HResult);
}
Disable playback of items in a playback list
Starting with Windows 10, version 1703, you can disable playback of one or more items in a MediaPlaybackItemList by setting the IsDisabledInPlaybackList property of a MediaPlaybackItem to false.
A typical scenario for this feature is for apps that play music streamed from the internet. The app can listen for changes in the network connection status of the device and disable playback of items that are not fully downloaded. In the following example, a handler is registered for the NetworkInformation.NetworkStatusChanged event.
Windows.Networking.Connectivity.NetworkInformation.NetworkStatusChanged += NetworkInformation_NetworkStatusChanged;
In the handler for NetworkStatusChanged, check to see if GetInternetConnectionProfile returns null, which indicates that the network is not connected. If this is the case, loop through all of the items in the playback list, and if the TotalDownloadProgress for the item is less than 1, meaning that the item has not fully downloaded, disable the item. If the network connection is enabled, loop through all of the items in the playback list and enable each item.
private void NetworkInformation_NetworkStatusChanged(object sender)
{
if (Windows.Networking.Connectivity.NetworkInformation.GetInternetConnectionProfile() == null)
{
// Check download status of each item in the list. (TotalDownloadProgress < 1 means not completely downloaded)
foreach (var item in _mediaPlaybackList.Items)
{
if (item.TotalDownloadProgress < 1)
{
item.IsDisabledInPlaybackList = true;
}
}
}
else
{
// Connected to internet, re-enable all playlist items
foreach (var item in _mediaPlaybackList.Items)
{
item.IsDisabledInPlaybackList = true;
}
}
}
Defer binding of media content for items in a playback list by using MediaBinder
In the previous examples, a MediaSource is created from a file, URL, or stream, after which a MediaPlaybackItem is created and added to a MediaPlaybackList. For some scenarios, such as if the user is being charged for viewing content, you may want to defer the retrieval of the content of a MediaSource until the item in the playback list is ready to actually be played. To implement this scenario, create an instance of the MediaBinder class. Set the Token property to an app-defined string that identifies the content for which you want to defer retrieval and then register a handler for the Binding event. Next, create a MediaSource from the Binder by calling MediaSource.CreateFromMediaBinder. Then, create a MediaPlaybackItem from the MediaSource and add it to the playback list as usual.
_mediaPlaybackList = new MediaPlaybackList();
var binder = new MediaBinder();
binder.Token = "MyBindingToken1";
binder.Binding += Binder_Binding;
_mediaPlaybackList.Items.Add(new MediaPlaybackItem(MediaSource.CreateFromMediaBinder(binder)));
binder = new MediaBinder();
binder.Token = "MyBindingToken2";
binder.Binding += Binder_Binding;
_mediaPlaybackList.Items.Add(new MediaPlaybackItem(MediaSource.CreateFromMediaBinder(binder)));
_mediaPlayer = new MediaPlayer();
_mediaPlayer.Source = _mediaPlaybackList;
mediaPlayerElement.SetMediaPlayer(_mediaPlayer);
When the system determines that the content associated with the MediaBinder needs to be retrieved, it will raise the Binding event. In the handler for this event, you can retrieve the MediaBinder instance from the MediaBindingEventArgs passed into the event. Retrieve the string you specified for the Token property and use it to determine what content should be retrieved. The MediaBindingEventArgs provides methods for setting the bound content in several different representations, including SetStorageFile, SetStream, SetStreamReference, and SetUri.
private void Binder_Binding(MediaBinder sender, MediaBindingEventArgs args)
{
// Get a deferral if you need to perform async operations
// var deferral = args.GetDeferral();
var contentUri = new Uri("http://contoso.com/media/" + args.MediaBinder.Token);
args.SetUri(contentUri);
// Call complete after your async operations are complete
// deferral.Complete();
}
Note that if you are performing asynchronous operations, such as web requests, in the Binding event handler, you should call the MediaBindingEventArgs.GetDeferral method to instruct the system to wait for your operation to complete before continuing. Call Deferral.Complete after your operation is complete to instruct the system to continue.
Starting with Windows 10, version 1703, you can supply an AdaptiveMediaSource as bound content by calling SetAdaptiveMediaSource. For more information on using adaptive streaming in your app, see Adaptive streaming.