Поделиться через


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

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

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

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

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

В следующем примере показано, как воспроизвести выбранный пользователем файл мультимедиа в MediaPlayer с помощью MediaSource. Чтобы разрешить пользователю выбрать файл мультимедиа для воспроизведения, используйте FileOpenPicker. Создайте экземпляр StorageFile из пути, возвращаемого методом PickSingleFileAsync средства выбора, и инициализируйте новый объект MediaSource, вызвав метод MediaSource.CreateFromStorageFile. Наконец, задайте источник мультимедиа в качестве источника воспроизведения для MediaPlayer , задав свойство SetPlaybackSource .

<MediaPlayerElement x:Name="mediaPlayerElement"/>
MediaSource? mediaSource;
MediaPlayer? mediaPlayer;
//Create a new picker
var filePicker = new Microsoft.Windows.Storage.Pickers.FileOpenPicker(this.AppWindow.Id)
{
    SuggestedStartLocation = PickerLocationId.VideosLibrary,
    FileTypeFilter = { ".wmv", ".mp4", ".mkv" },
};

//Retrieve file from picker
var result = await filePicker.PickSingleFileAsync();

if (result is not null)
{
    var storageFile = await Windows.Storage.StorageFile.GetFileFromPathAsync(result.Path);
    mediaSource = MediaSource.CreateFromStorageFile(storageFile);
    mediaPlayer = new MediaPlayer();
    mediaPlayer.Source = mediaSource;
    mediaPlayerElement.SetMediaPlayer(mediaPlayer);
}

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

mediaPlayer.Play();

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

mediaPlayer.AutoPlay = true;

Управляйте несколькими аудио-дорожками, видео-дорожками и метаданными с помощью MediaPlaybackItem.

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

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

MediaPlaybackItem mediaPlaybackItem;
mediaSource = MediaSource.CreateFromStorageFile(storageFile);
mediaPlaybackItem = new MediaPlaybackItem(mediaSource);

mediaPlaybackItem.AudioTracksChanged += MediaPlaybackItem_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 . Если у трека еще нет метки, метка создается из индекса трека. Для свойства Tag элемента комбинированного списка установлен индекс трека, чтобы его можно было определить позже. Наконец, элемент добавляется в поле со списком. Обратите внимание, что эти операции выполняются в вызове DispatcherQueue.TryEnqueue , так как все изменения пользовательского интерфейса должны быть сделаны в потоке пользовательского интерфейса, и это событие вызывается в другом потоке.

private void MediaPlaybackItem_VideoTracksChanged(MediaPlaybackItem sender, IVectorChangedEventArgs args)
{
    DispatcherQueue.TryEnqueue(() =>
    {
        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 для комбинированного списка индекс отслеживания извлекается из свойства Tag выбранного элемента. Установка свойства 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 void MediaPlaybackItem_AudioTracksChanged(MediaPlaybackItem sender, IVectorChangedEventArgs args)
{
    DispatcherQueue.TryEnqueue(() =>
    {
        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;
            audioTracksComboBox.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 void MediaPlaybackItem_TimedMetadataTracksChanged(MediaPlaybackItem sender, IVectorChangedEventArgs args)
{
    DispatcherQueue.TryEnqueue(() =>
    {
        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, передав индекс дорожки, которую нужно переключить, а затем укажите значение из перечисления 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. Это можно сделать для обновления интерфейса пользователя, чтобы отобразить места размещения подсказок для мультимедийного элемента.

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

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

Получив ссылку на вставленную дорожку, проверьте DecoderStatus свойства SupportInfo. Если значение ПолностьюПоддерживается, на устройстве присутствует соответствующий кодек, необходимый для воспроизведения трека. Если значение понижено, то трек может воспроизводиться системой, но воспроизведение будет понижено в некотором смысле. Например, звуковая дорожка 5.1 может воспроизводиться в двухканальном стерео. В этом случае может потребоваться обновить пользовательский интерфейс, чтобы предупредить пользователя об ухудшении состояния. Если значение равно 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);
    }
    ;
}

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

Медиа, воспроизводимое в 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.

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

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

Dictionary<TimedTextSource, Uri> timedTextSourceMap;
// 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 объекта TimedTextSourceResolveResultEventArgs, чтобы определить, возникла ли ошибка при загрузке данных синхронизированного текста. Если элемент был разрешен успешно, этот обработчик можно использовать для обновления дополнительных свойств разрешенной дорожки. В этом примере добавляется метка для каждой дорожки на основе 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 . Регистрируйте обработчики для событий CueEntered и CueExited. Эти события возникают при достижении времени начала подсказки и истечении срока действия подсказки соответственно.

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

Свойство 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, система не принимает никаких действий, так как эти кодировщики не вводят задержку или заполнение.

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

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

Чтобы включить воспроизведение вашего списка, установите источник воспроизведения MediaPlayer на ваш MediaPlaybackList.

MediaPlaybackList mediaPlaybackList;
mediaPlaybackList = new MediaPlaybackList();

var results = await filePicker.PickMultipleFilesAsync();

foreach (var result in results)
{
    var storageFile = await Windows.Storage.StorageFile.GetFileFromPathAsync(result.Path);
    var mediaPlaybackItem = new MediaPlaybackItem(MediaSource.CreateFromStorageFile(storageFile));
    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 , переданного в событие. Помните, что если вы обновляете пользовательский интерфейс из этого события, делайте это в вызове DispatcherQueue.TryEnqueue, чтобы обновления выполнялись в потоке пользовательского интерфейса.

Вы можете проверить свойство 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 void ShuffleButton_Click(object sender, RoutedEventArgs e)
{
    mediaPlaybackList.ShuffleEnabled = !mediaPlaybackList.ShuffleEnabled;

    DispatcherQueue.TryEnqueue(() =>
    {
        shuffleButton.FontWeight =
            mediaPlaybackList.ShuffleEnabled ? Microsoft.UI.Text.FontWeights.Bold : Microsoft.UI.Text.FontWeights.Light;
    });
}

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

private void AutoRepeatButton_Click(object sender, RoutedEventArgs e)
{
    mediaPlaybackList.AutoRepeatEnabled = !mediaPlaybackList.AutoRepeatEnabled;

    DispatcherQueue.TryEnqueue(() =>
    {
        autoRepeatButton.FontWeight =
            mediaPlaybackList.AutoRepeatEnabled ? Microsoft.UI.Text.FontWeights.Bold : Microsoft.UI.Text.FontWeights.Light;
    });
}

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

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

private void MediaPlaybackList_ItemOpened(MediaPlaybackList sender, MediaPlaybackItemOpenedEventArgs args)
{

}

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

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

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

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

В обработчике NetworkStatusChanged проверьте, возвращает ли GetInternetConnectionProfile значение 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 создается и добавляется в MediaPlaybackList. Для некоторых сценариев, например, если с пользователя взимается плата за просмотр содержимого, может потребоваться отложить извлечение содержимого 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 предоставляет методы настройки связанного содержимого в нескольких различных представлениях, включая SetStorageFile, 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, вы можете предоставить AdaptiveMediaSource в виде связанного содержимого, вызвав SetAdaptiveMediaSource. Дополнительные сведения об использовании адаптивной потоковой передачи в приложении см. в разделе "Адаптивная потоковая передача".