Элементы мультимедиа, списки воспроизведения и треки

В этой статье показано, как использовать класс MediaSource , который предоставляет общий способ ссылаться на носители и воспроизводить их из разных источников, таких как локальные или удаленные файлы, и предоставляет общую модель для доступа к данным мультимедиа независимо от базового формата мультимедиа. Класс MediaPlaybackItem расширяет функциональные возможности MediaSource, позволяя управлять и выбирать из нескольких звуковых, видео и метаданных треков, содержащихся в элементе мультимедиа. MediaPlaybackList позволяет создавать списки воспроизведения из одного или нескольких элементов воспроизведения мультимедиа.

Создание и воспроизведение MediaSource

Создайте новый экземпляр MediaSource , вызвав один из методов фабрики, предоставляемых классом:

После создания MediaSource его можно воспроизвести с MediaPlayer, задав свойство Source. Начиная с Windows 10 версии 1607, вы можете назначить MediaPlayer MediaPlayerElement путем вызова SetMediaPlayer, чтобы отобразить содержимое проигрывателя мультимедиа на странице XAML. Это предпочтительный метод для использования MediaElement. Дополнительные сведения об использовании MediaPlayer см. в статье "Воспроизведение звука и видео" с помощью MediaPlayer.

В следующем примере показано, как воспроизвести выбранный пользователем файл мультимедиа в MediaPlayer с помощью MediaSource.

Для выполнения этого сценария необходимо включить пространства имен Windows.Media.Core и Windows.Media.Воспроизведение.

using Windows.Media.Core;
using Windows.Media.Playback;

Объявите переменную типа MediaSource. В примерах этой статьи источник мультимедиа объявляется как член класса, чтобы получить доступ к нему из нескольких расположений.

MediaSource _mediaSource;

Объявите переменную для хранения объекта MediaPlayer и, если вы хотите отобразить содержимое мультимедиа в XAML, добавьте элемент управления MediaPlayerElement на страницу.

MediaPlayer _mediaPlayer;
<MediaPlayerElement x:Name="mediaPlayerElement"/>

Чтобы разрешить пользователю выбрать файл мультимедиа для воспроизведения, используйте FileOpenPicker. При использовании объекта служба хранилища File, возвращаемого из метода PickSingleFileAsync средства выбора, инициализировать новый MediaObject путем вызова MediaSource.CreateFrom служба хранилища File. Наконец, задайте источник мультимедиа в качестве источника воспроизведения для MediaElement, вызвав метод SetPlaybackSource.

//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);
}

По умолчанию MediaPlayer не начинает воспроизводиться автоматически при установке источника мультимедиа. Вы можете вручную начать воспроизведение, вызвав воспроизведение.

_mediaPlayer.Play();

Также можно задать для свойства AutoPlayer MediaPlayer значение true, чтобы проигрыватель начал воспроизводиться сразу после установки источника мультимедиа.

_mediaPlayer.AutoPlay = true;

Создание MediaSource из DownloadOperation

Начиная с Windows версии 1803, можно создать объект MediaSource из 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);

Обратите внимание, что хотя вы можете создать MediaSource из скачивания, не запуская его или не устанавливая для негозначение true, необходимо выполнить оба этих действия, прежде чем пытаться подключить MediaSource к MediaPlayer или MediaPlayerElement для воспроизведения.

downloadOperation.IsRandomAccessRequired = true;
var startAsyncTask = downloadOperation.StartAsync().AsTask();
mediaPlayerElement.Source = mediaSource;

Обработка нескольких звуковых, видео и метаданных с помощью MediaPlaybackItem

Использование MediaSource для воспроизведения удобно, так как оно предоставляет общий способ воспроизведения мультимедиа из разных типов источников, но к более расширенному поведению можно получить доступ, создав MediaPlaybackItem из MediaSource. Это включает в себя возможность доступа к нескольким звуковым, видео и данным для элемента мультимедиа и управления ими.

Объявите переменную для хранения MediaPlaybackItem.

MediaPlaybackItem _mediaPlaybackItem;

Создайте MediaPlaybackItem, вызвав конструктор и передавая инициализированный объект MediaSource.

Если приложение поддерживает несколько звуковых, видео или треков данных в элементе воспроизведения мультимедиа, зарегистрируйте обработчики событий для событий AudioTracksChanged, VideoTracksChanged или TimedMetadataTracksChanged.

Наконец, задайте для источника воспроизведения MediaElement или MediaPlayer значение 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);

Примечание.

MediaSource может быть связан только с одним MediaPlaybackItem. После создания MediaPlaybackItem из источника попытка создать другой элемент воспроизведения из того же источника приведет к ошибке. Кроме того, после создания MediaPlaybackItem из источника мультимедиа нельзя задать объект MediaSource непосредственно в качестве источника MediaPlayer , но вместо этого использовать MediaPlaybackItem.

Событие VideoTracksChanged возникает после того, как MediaPlaybackItem , содержащий несколько видео-треков, назначается в качестве источника воспроизведения и может быть поднят снова, если список видео треков изменяется для элемента. Обработчик этого события дает возможность обновить пользовательский интерфейс, чтобы разрешить пользователю переключаться между доступными треками. В этом примере используется comboBox для отображения доступных видео треков.

<ComboBox x:Name="videoTracksComboBox" SelectionChanged="videoTracksComboBox_SelectionChanged"/>

В обработчике VideoTracksChanged прокрутите все треки в списке VideoTracks элемента воспроизведения. Для каждой дорожки создается новый comboBoxItem. Если у трека еще нет метки, то метка создается из индекса отслеживания. Для свойства Тега элемента поля со списком задан индекс отслеживания, чтобы его можно было определить позже. Наконец, элемент добавляется в поле со списком. Обратите внимание, что эти операции выполняются в вызове CoreDispatcher.RunAsync , так как все изменения пользовательского интерфейса должны быть сделаны в потоке пользовательского интерфейса, и это событие вызывается в другом потоке.

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);
        }
    });
}

В обработчике SelectionChanged для поля со списком индекс отслеживания извлекается из свойства тега выбранного элемента. Установка свойства SelectedIndex списка VideoTracks элемента воспроизведения мультимедиа приводит к тому, что MediaElement или MediaPlayer переключит активный видеодорожок на указанный индекс.

private void videoTracksComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    int trackIndex = (int)((ComboBoxItem)((ComboBox)sender).SelectedItem).Tag;
    _mediaPlaybackItem.VideoTracks.SelectedIndex = trackIndex;
}

Управление элементами мультимедиа с несколькими звуковыми дорожками работает точно так же, как и с видеодорожками. Обработайте AudioTracksChanged, чтобы обновить пользовательский интерфейс с звуковыми дорожками, найденными в списке AudioTracks элемента воспроизведения. Когда пользователь выбирает звуковую дорожку, задайте свойство SelectedIndex списка AudioTracks, чтобы включить MediaElement или MediaPlayer для переключения активной звуковой дорожки на указанный индекс.

<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;
}

Помимо аудио и видео, объект MediaPlaybackItem может содержать ноль или больше объектов TimedMetadataTrack. Метка метаданных по времени может содержать подзаголовок или текст подпись или содержать пользовательские данные, которые являются владельцем приложения. Отслеживание метаданных по времени содержит список подсказок, представленных объектами, наследующими от IMediaCue, например DataCue или TimedTextCue. Каждый подсказка имеет время начала и длительность, которая определяет, когда активируется подсказка и сколько времени.

Как и звуковые дорожки и видео- дорожки, отслеживаемые метаданные для элемента мультимедиа можно обнаружить, обрабатывая событие TimedMetadataTracksChanged в MediaPlaybackItem. Однако при отслеживании времени метаданные пользователю может потребоваться включить несколько отслеживания метаданных за раз. Кроме того, в зависимости от сценария приложения может потребоваться включить или отключить отслеживание метаданных автоматически без вмешательства пользователя. Для иллюстрации в этом примере добавляется toggleButton для каждой дорожки метаданных в элементе мультимедиа, чтобы разрешить пользователю включить и отключить дорожку. Свойство Тега каждой кнопки имеет индекс связанной дорожки метаданных, чтобы ее можно было определить при переключение кнопки.

<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);
        }
    });
}

Так как несколько треков метаданных могут быть активными в один раз, вы не просто задаете активный индекс для списка отслеживания метаданных. Вместо этого вызовите метод SetPresentationMode объекта MediaPlaybackItem, передав индекс трека, который нужно переключить, а затем укажите значение из перечисления TimedMetadataTrackPresentationMode. Выбранный режим презентации зависит от реализации приложения. В этом примере для отслеживания метаданных задано значение PlatformPresented при включении. Для текстовых треков это означает, что система автоматически отобразит подсказки текста в треке. При отключении переключателя режим презентации имеет значение "Отключено", что означает, что текст не отображается и события подсказки не создаются. События Cue рассматриваются далее в этой статье.

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);

При обработке треков метаданных можно получить доступ к набору подсказок в дорожке, доступ к свойствам Cues или ActiveCues. Это можно сделать, чтобы обновить пользовательский интерфейс, чтобы отобразить расположения подсказки для элемента мультимедиа.

Обработка неподдерживаемых кодеков и неизвестных ошибок при открытии элементов мультимедиа

Начиная с Windows 10 версии 1607, можно проверка, поддерживается ли кодек для воспроизведения элемента мультимедиа или частично поддерживается на устройстве, на котором работает ваше приложение. В обработчике событий для событий MediaPlaybackItem отслеживаются измененные события, такие как AudioTracksChanged, сначала проверка, чтобы узнать, является ли изменение трека вставкой нового трека. Если это так, вы можете получить ссылку на вставленную дорожку с помощью индекса, переданного в параметре IVectorChangedEventArgs.Index с соответствующей коллекцией треков параметра MediaPlaybackItem, например коллекции AudioTracks.

Получив ссылку на вставленную дорожку, проверка ДекодерStatus свойства SupportInfo. Если значение полностью поддерживается, на устройстве присутствует соответствующий кодек, необходимый для воспроизведения дорожки. Если значение понижено, то трек может воспроизводиться системой, но воспроизведение будет понижено в некотором смысле. Например, звуковая дорожка 5.1 может воспроизводиться обратно как стерео 2-канал. В этом случае может потребоваться обновить пользовательский интерфейс, чтобы предупредить пользователя об ухудшении состояния. Если значение равно UnsupportedSubtype или UnsupportedEncoderProperties, то трек не может быть воспроизведен на всех с текущими кодеками на устройстве. Вы можете предупредить пользователя и пропустить воспроизведение элемента или реализовать пользовательский интерфейс, чтобы пользователь мог скачать правильный кодек. Метод GetEncodingProperties трека можно использовать для определения требуемого кодека для воспроизведения.

Наконец, вы можете зарегистрировать событие OpenFailed трека, которое будет возникать, если трек поддерживается на устройстве, но не удалось открыть из-за неизвестной ошибки в конвейере.

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;
        }
    }

}

В обработчике событий OpenFailed можно проверка, чтобы узнать, неизвестно ли состояние MediaSource, а если да, можно программно выбрать другой трек для воспроизведения, разрешить пользователю выбрать другую дорожку или отказаться от воспроизведения.

private async void InsertedTrack_OpenFailed(AudioTrack sender, AudioTrackOpenFailedEventArgs args)
{
    LogError(args.ExtendedError.HResult);

    if (sender.SupportInfo.MediaSourceStatus == MediaSourceStatus.Unknown)
    {
        await SelectAnotherTrackOrSkipPlayback(sender.PlaybackItem);
    }
}

Задание свойств отображения, используемых элементами управления транспортировкой системных носителей

Начиная с Windows 10 версии 1607, носитель, воспроизводимый в MediaPlayer , автоматически интегрируется в системные элементы управления транспортировкой мультимедиа (SMTC) по умолчанию. Можно указать метаданные, которые будут отображаться SMTC, обновив свойства отображения для MediaPlaybackItem. Получите объект, представляющий свойства отображения для элемента, вызвав GetDisplayProperties. Задайте, является ли элемент воспроизведения музыкой или видео, задав свойство Type . Затем задайте свойства объекта VideoProperties или MusicProperties. Вызовите ApplyDisplayProperties , чтобы обновить свойства элемента до указанных значений. Как правило, приложение будет динамически извлекать значения отображения из веб-службы, но в следующем примере показан этот процесс с жестко закодированных значений.

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);

Добавление внешнего текста с использованием TimedTextSource

В некоторых сценариях могут быть внешние файлы, содержащие текст времени, связанный с элементом мультимедиа, например отдельные файлы, содержащие субтитры для разных языковых стандартов. Используйте класс TimedTextSource для загрузки во внешние текстовые файлы времени из потока или URI.

В этом примере коллекция словарей используется для хранения списка источников времени для элемента мультимедиа с помощью исходного URI и объекта TimedTextSource в качестве пары "ключ-значение", чтобы определить треки после их разрешения.

Dictionary<TimedTextSource, Uri> timedTextSourceMap;

Создайте новый timedTextSource для каждого внешнего текстового файла, вызвав CreateFromUri. Добавьте запись в словарь для исходного текста с временем. Добавьте обработчик события TimedTextSource.Resolved , чтобы обрабатывать, если элемент не удалось загрузить или задать дополнительные свойства после успешной загрузки элемента.

Зарегистрируйте все объекты TimedTextSource в MediaSource, добавив их в коллекцию ExternalTimedTextSources. Обратите внимание, что внешние источники текста с временем добавляются непосредственно в MediaSource , а не в MediaPlaybackItem , созданном из источника. Чтобы обновить пользовательский интерфейс, чтобы отразить внешние текстовые дорожки, зарегистрируйте и обработайте событие TimedMetadataTracksChanged , как описано ранее в этой статье.

// 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);

В обработчике события TimedTextSource.Resolved проверка свойство Error объекта TimedTextSourceResolveResolveResultEventArgs, чтобы определить, произошла ли ошибка при попытке загрузить текстовые данные по времени. Если элемент был разрешен успешно, этот обработчик можно использовать для обновления дополнительных свойств разрешенной дорожки. В этом примере добавляется метка для каждой дорожки на основе URI, ранее хранящегося в словаре.

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";
    }
}

Список текстовых форматов времени, поддерживаемых в Windows, см. в разделе "Поддерживаемые кодеки".

Добавление дополнительных треков метаданных

Вы можете динамически создавать пользовательские треки метаданных в коде и связывать их с источником мультимедиа. Создаваемые треки могут содержать подзаголовок или подпись текст или содержать собственные данные приложения.

Создайте объект TimedMetadataTrack, вызвав конструктор и указав идентификатор, идентификатор языка и значение из перечисления TimedMetadataKind. Регистрируйте обработчики для событий Cue Ввод и CueExited. Эти события возникают при достижении времени начала подсказки и истечении срока действия подсказки соответственно.

Создайте новый объект cue, соответствующий типу созданного отслеживания метаданных, и задайте идентификатор, время начала и длительность отслеживания. В этом примере создается отслеживание данных, поэтому для каждого подсказки создается набор объектов DataCue , а буфер, содержащий данные, относящиеся к приложению. Чтобы зарегистрировать новую дорожку, добавьте его в коллекцию ExternalTimedMetadataTracks объекта MediaSource.

Начиная с Windows 10 версии 1703 свойство DataCue.Properties предоставляет набор свойств, который можно использовать для хранения пользовательских свойств в парах ключей и данных, которые можно получить в событиях Cue Ввод и CueExited.

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);

Событие CueEntered возникает при достижении времени начала подсказки до тех пор, пока связанная дорожка имеет режим представления ApplicationPresented, Hidden или PlatformPresented. События Cue не создаются для отслеживания метаданных, пока режим презентации для трека отключен. В этом примере просто выводится пользовательские данные, связанные с подсказкой в окно отладки.

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"]);
}

В этом примере добавляется настраиваемая текстовая дорожка, указывая TimedMetadataKind.Caption при создании трека и использовании объектов TimedTextCue для добавления подсказок в трек.

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);
}

Воспроизведение списка элементов мультимедиа с помощью MediaPlaybackList

MediaPlaybackList позволяет создать список воспроизведения элементов мультимедиа, представленных объектами MediaPlaybackItem.

Элементы заметки в MediaPlaybackList отрисовываются с помощью воспроизведения без пробелов. Система будет использовать предоставленные метаданные в файлах в кодировке MP3 или AAC, чтобы определить компенсацию задержки или заполнения, необходимую для воспроизведения без пробелов. Если файлы в кодировке MP3 или AAC не предоставляют эти метаданные, система определяет задержку или заполнение эвристических способов. Для форматов без потери, таких как PCM, FLAC или ALAC, система не принимает никаких действий, так как эти кодировщики не вводят задержку или заполнение.

Чтобы приступить к работе, объявите переменную для хранения MediaPlaybackList.

MediaPlaybackList _mediaPlaybackList;

Создайте MediaPlaybackItem для каждого элемента мультимедиа, который вы хотите добавить в список, используя ту же процедуру, описанную ранее в этой статье. Инициализировать объект MediaPlaybackList и добавить в него элементы воспроизведения мультимедиа. Зарегистрируйте обработчик для события CurrentItemChanged. Это событие позволяет обновить пользовательский интерфейс, чтобы отразить текущий воспроизводимый элемент мультимедиа. Вы также можете зарегистрировать событие ItemOpened , которое возникает при успешном открытии элемента в списке, и событие ItemFailed , которое возникает, когда элемент в списке не может быть открыт.

Начиная с Windows 10 версии 1703, можно указать максимальное количество объектов MediaPlaybackItem в MediaPlaybackList , которое система будет оставаться открытой после их воспроизведения, задав свойство MaxPlayedItemsToKeepOpen . Когда MediaPlaybackItem остается открытым, воспроизведение элемента может начаться мгновенно, когда пользователь переключается на этот элемент, так как элемент не требует перезагрузки. Но при открытии элементов также увеличивается потребление памяти приложения, поэтому при настройке этого значения следует учитывать баланс между скоростью реагирования и использованием памяти.

Чтобы включить воспроизведение списка, задайте для источника воспроизведения MediaPlayer значение MediaPlayer в 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);

В обработчике событий CurrentItemChanged обновите пользовательский интерфейс, чтобы отразить текущий воспроизводимый элемент, который можно получить с помощью свойства NewItem объекта CurrentMediaPlaybackItemChangedEventArgs, переданного в событие. Помните, что при обновлении пользовательского интерфейса из этого события необходимо сделать это в рамках вызова CoreDispatcher.RunAsync , чтобы обновления были сделаны в потоке пользовательского интерфейса.

Начиная с Windows 10 версии 1703, можно проверка свойству CurrentMediaPlaybackItemChangedEventArgs.Reason, указывающее причину изменения элемента, например переключения элементов, программно, ранее играющего элемента до конца или ошибки.

private void MediaPlaybackList_CurrentItemChanged(MediaPlaybackList sender, CurrentMediaPlaybackItemChangedEventArgs args) => 
    LogTelemetryData($"CurrentItemChanged reason: {args.Reason.ToString()}");

Вызов MovePrevious или MoveNext , чтобы проигрыватель мультимедиа играл предыдущий или следующий элемент в MediaPlaybackList.

private void prevButton_Click(object sender, RoutedEventArgs e) =>  _mediaPlaybackList.MovePrevious();
private void nextButton_Click(object sender, RoutedEventArgs e) => _mediaPlaybackList.MoveNext();

Задайте свойство ShuffleEnabled, чтобы указать, должен ли проигрыватель мультимедиа воспроизводить элементы в списке в случайном порядке.

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;
    });
}

Задайте свойство AutoRepeatEnabled, чтобы указать, должен ли проигрыватель мультимедиа циклически воспроизводить список.

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;
    });
}

Обработка сбоя элементов мультимедиа в списке воспроизведения

Событие ItemFailed возникает, когда элемент в списке не открывается. Свойство ErrorCode объекта MediaPlaybackItemError, переданное обработчику, перечисляет конкретную причину сбоя, если это возможно, включая сетевые ошибки, декодирование ошибок или ошибок шифрования.

private void MediaPlaybackList_ItemFailed(MediaPlaybackList sender, MediaPlaybackItemFailedEventArgs args)
{
    LogError(args.Error.ErrorCode.ToString());
    LogError(args.Error.ExtendedError.HResult);
}

Отключение воспроизведения элементов в списке воспроизведения

Начиная с Windows 10 версии 1703, вы можете отключить воспроизведение одного или нескольких элементов в MediaPlaybackItemList, установив для свойства IsDisabledInPlaybackList значение false.

Типичный сценарий для этой функции — для приложений, которые играют музыку, потоковую из Интернета. Приложение может прослушивать изменения состояния сетевого подключения устройства и отключать воспроизведение элементов, которые не полностью загружены. В следующем примере обработчик регистрируется для события NetworkInformation.NetworkStatusChanged .

Windows.Networking.Connectivity.NetworkInformation.NetworkStatusChanged += NetworkInformation_NetworkStatusChanged;

В обработчике NetworkStatusChanged проверка, чтобы узнать, возвращается ли GetInternet Подключение ionProfile значение NULL, указывающее, что сеть не подключена. Если это так, выполните цикл по всем элементам в списке воспроизведения, а если значение TotalDownloadProgress для элемента меньше 1, то есть элемент не полностью скачан, отключите элемент. Если сетевое подключение включено, выполните цикл по всем элементам в списке воспроизведения и включите каждый элемент.

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;
        }
    }
}

Отложить привязку содержимого мультимедиа для элементов в списке воспроизведения с помощью MediaBinder

В предыдущих примерах MediaSource создается из файла, URL-адреса или потока, после чего создается и добавляется в MediaPlaybackItem. Для некоторых сценариев, например, если пользователь взимается за просмотр содержимого, может потребоваться отложить извлечение содержимого MediaSource до тех пор, пока элемент в списке воспроизведения не будет готов к воспроизведению. Чтобы реализовать этот сценарий, создайте экземпляр класса MediaBinder. Задайте свойству Token определяемую приложением строку, которая определяет содержимое, для которого требуется отложить извлечение, а затем зарегистрировать обработчик для события Привязки. Затем создайте MediaSource из Binder, вызвав MediaSource.CreateFromMediaBinder. Затем создайте MediaPlaybackItem из MediaSource и добавьте его в список воспроизведения как обычно.

_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);

Когда система определяет, что содержимое, связанное с MediaBinder, необходимо извлечь, оно вызовет событие Привязки. В обработчике этого события можно получить экземпляр MediaBinder из MediaBindingEventArgs , переданного в событие. Получите строку, указанную для свойства Token, и используйте ее, чтобы определить, какое содержимое должно быть извлечено. MediaBindingEventArgs предоставляет методы настройки связанного содержимого в нескольких различных представлениях, включая Set служба хранилища File, SetStream, SetStreamReference и 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();
}

Обратите внимание, что при выполнении асинхронных операций, таких как веб-запросы, в обработчике событий привязки необходимо вызвать метод MediaBindingEventArgs.GetDeferral , чтобы указать системе ждать завершения операции перед продолжением. Вызов Deferral.Complete после завершения операции, чтобы указать системе продолжить работу.

Начиная с Windows 10 версии 1703, вы можете предоставить АдаптивныйMediaSource в виде связанного содержимого, вызвав SetAdaptiveMediaSource. Дополнительные сведения об использовании адаптивной потоковой передачи в приложении см. в разделе "Адаптивная потоковая передача".