Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этой статье показано, как воспроизводить мультимедиа в приложении WinUI с помощью упрощенного элемента управления XAML класса MediaPlayer, MediaPlayerElement.
В этой статье описаны функции MediaPlayer , которые будут использоваться типичным приложением воспроизведения мультимедиа. Обратите внимание, что MediaPlayer использует класс MediaSource в качестве контейнера для всех элементов мультимедиа. Этот класс позволяет загружать и воспроизводить носители из разных источников, включая локальные файлы, потоки памяти и сетевые источники, все с помощью одного интерфейса. Существуют также классы более высокого уровня, которые работают с MediaSource, например MediaPlaybackItem и MediaPlaybackList, которые предоставляют более сложные функции, такие как списки воспроизведения и возможность управления источниками мультимедиа с несколькими звуковыми, видео и метаданными. Дополнительные сведения об MediaSource и связанных API см. в разделе "Элементы мультимедиа", списки воспроизведения и треки.
Воспроизведение файла мультимедиа с помощью MediaPlayer
Базовое воспроизведение мультимедиа с помощью MediaPlayer очень просто для реализации. Сначала создайте новый экземпляр класса MediaPlayer . Приложение может иметь несколько активных экземпляров MediaPlayer одновременно. Затем задайте свойству Source проигрывателя объект, реализующий IMediaPlaybackSource, например MediaSource, MediaPlaybackItem или MediaPlaybackList.
В следующем примере MediaSource создается из файла в локальном хранилище приложения, а затем mediaPlaybackItem создается из источника, а затем назначается свойству source проигрывателя. В отличие от MediaElement, MediaPlayer по умолчанию не начинает воспроизведение автоматически. Вы можете начать воспроизведение путем вызова воспроизведения, установив для свойства AutoPlay значение true или ожидая, чтобы пользователь начал воспроизведение с помощью встроенных элементов управления мультимедиа.
mediaPlayer = new MediaPlayer();
mediaPlayer.Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video.mkv"));
mediaPlayer.Play();
Когда приложение выполняется с помощью MediaPlayer, необходимо вызвать метод Close (проецируемый для удаления в C#), чтобы очистить ресурсы, используемые проигрывателем.
mediaPlayer.Dispose();
Использование MediaPlayerElement для рендеринга видео в XAML
Вы можете воспроизводить медиа в MediaPlayer, не отображая его в XAML, но многие приложения для воспроизведения мультимедиа хотят отображать медиа на странице XAML. Для этого используйте упрощенный элемент управления MediaPlayerElement . Подобно MediaElement, MediaPlayerElement позволяет указать, должны ли отображаться встроенные элементы управления воспроизведением.
<MediaPlayerElement x:Name="mediaPlayerElement" AreTransportControlsEnabled="False" HorizontalAlignment="Stretch" />
Можно задать экземпляр MediaPlayer , к которому привязан элемент, вызвав SetMediaPlayer.
mediaPlayerElement.SetMediaPlayer(mediaPlayer);
Вы также можете задать источник воспроизведения в MediaPlayerElement , а элемент автоматически создаст новый экземпляр MediaPlayer , к которому можно получить доступ с помощью свойства MediaPlayer .
Замечание
При задании свойств MediaPlayerElement будут заданы соответствующие свойства в базовом mediaPlayer. Вы можете использовать базовый MediaPlayer напрямую вместо использования свойств MediaPlayerElement. Помните, что использование MediaPlayer напрямую, там, где в противном случае может быть использовано эквивалентное свойство MediaPlayerElement, может привести к непредвиденному поведению. Это связано с тем, что MediaPlayerElement не знает обо всем, что происходит с его базовым MediaPlayer. Например, если задать источник непосредственно в MediaPlayer, свойство MediaPlayerElementSource не будет отражать изменения. По этой причине необходимо последовательно использовать свойства MediaPlayerElement или напрямую использовать базовый MediaPlayer.
mediaPlayerElement.Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video.mkv"));
mediaPlayer = mediaPlayerElement.MediaPlayer;
mediaPlayer.Play();
Замечание
Если отключить MediaPlaybackCommandManager в MediaPlayer, установив IsEnabled в значение false, это разорвет связь между MediaPlayer и TransportControls, предоставляемыми MediaPlayerElement, поэтому встроенные элементы управления транспортом больше не будут автоматически контролировать воспроизведение проигрывателя. Вместо этого необходимо реализовать собственные элементы управления для управления MediaPlayer.
MediaPlayer отсоединяется от MediaPlayerElement при уничтожении MediaPlayerElement или при установке нового mediaPlayer с помощью SetMediaPlayer. При отключении MediaPlayerElement обрабатывает базовый MediaPlayer по-разному в зависимости от того, был ли он создан MediaPlayerElement или установлен с помощью SetMediaPlayer.
Если MediaPlayer был создан MediaPlayerElement, он правильно закроетMediaPlayer для вас. Если mediaPlayer был установлен в MediaPlayerElement с помощью SetMediaPlayer, вы несете ответственность за обеспечение правильного закрытия MediaPlayer . Несоблюдение этого может привести к фатальной ошибке воспроизведения в MediaPlayer. В следующем фрагменте кода показано, как правильно отсоединить и закрыть код.
// Get a reference to the current media source.
IMediaPlaybackSource source = mediaPlayerElement.Source;
// Pause playback if able.
if (mediaPlayer.PlaybackSession.CanPause)
{
mediaPlayer.Pause();
}
// Disconnect the MediaPlayer from its source. This can be done by setting
// the MediaPlayerElement Source property to null or by directly setting the
// source to null on the underlying MediaPlayer.
mediaPlayerElement.Source = null;
// Disconnect the MediaPlayer from MediaPlayerElement.
mediaPlayerElement.SetMediaPlayer(null);
// Dispose of the MediaPlayer or Source if they're no longer needed.
if (source is MediaSource mediaSource)
{
mediaSource.Dispose();
}
mediaPlayer.Dispose();
Распространенные задачи MediaPlayer
В этом разделе показано, как использовать некоторые функции класса MediaPlayer .
Настройка категории звука
Задайте для свойства AudioCategorymediaPlayer одно из значений перечисления MediaPlayerAudioCategory , чтобы система знала, какой носитель вы играете. Игры должны классифицировать свои музыкальные потоки как GameMedia, чтобы игровая музыка автоматически отключалась, если другое приложение играет музыку в фоновом режиме. Музыкальные или видеоприложения должны классифицировать потоки как медиа или фильм, чтобы они имели приоритет над потоками GameMedia.
mediaPlayer.AudioCategory = MediaPlayerAudioCategory.Media;
Вывод в определенную конечную точку звука
По умолчанию выходные данные звука из MediaPlayer направляются в конечную точку звука по умолчанию для системы, но можно указать определенную конечную точку звука, которую mediaPlayer должен использовать для вывода. В приведенном ниже примере MediaDevice.GetAudioRenderSelector возвращает строку, которая однозначно идентифицирует категорию отрисовки звука устройств. Затем вызывается метод DeviceInformation FindAllAsync, чтобы получить список всех доступных устройств выбранного типа. Вы можете программно определить, какое устройство вы хотите использовать или добавить возвращенные устройства в ComboBox , чтобы разрешить пользователю выбрать устройство.
string audioSelector = MediaDevice.GetAudioRenderSelector();
var outputDevices = await DeviceInformation.FindAllAsync(audioSelector);
foreach (var device in outputDevices)
{
var deviceItem = new ComboBoxItem();
deviceItem.Content = device.Name;
deviceItem.Tag = device;
AudioDeviceComboBox.Items.Add(deviceItem);
}
В событии SelectionChanged для поля со списком устройств свойство AudioDevice объекта MediaPlayer устанавливается на выбранное устройство, которое было сохранено в свойстве Tag элемента ComboBoxItem.
private void AudioDeviceComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
DeviceInformation selectedDevice = (DeviceInformation)((ComboBoxItem)AudioDeviceComboBox.SelectedItem).Tag;
if (selectedDevice != null)
{
mediaPlayer.AudioDevice = selectedDevice;
}
}
Сеанс воспроизведения
Класс MediaPlaybackSession предоставляет сведения о состоянии воспроизведения проигрывателя, например текущей позиции воспроизведения, приостановке или воспроизведении проигрывателя, а также о текущей скорости воспроизведения. MediaPlaybackSession также предоставляет несколько событий, чтобы уведомить вас об изменении состояния, включая состояние текущей буферизации и загрузки воспроизводимого содержимого, а также естественный размер и соотношение сторон текущего воспроизведения видео.
В следующем примере показано, как реализовать обработчик нажатия кнопки, который пропускает 10 секунд вперед в содержимом. Во-первых, объект MediaPlaybackSession для проигрывателя извлекается со свойством PlaybackSession. Далее для свойства Position задано текущее положение воспроизведения плюс 10 секунд.
private void SkipForwardButton_Click(object sender, RoutedEventArgs e)
{
var session = mediaPlayer.PlaybackSession;
session.Position = session.Position + TimeSpan.FromSeconds(10);
}
В следующем примере показана кнопка переключателя для переключения между обычной скоростью воспроизведения и скоростью 2X, задав свойство PlaybackRate сеанса.
private void SpeedToggleButton_Checked(object sender, RoutedEventArgs e)
{
mediaPlayer.PlaybackSession.PlaybackRate = 2.0;
}
private void SpeedToggleButton_Unchecked(object sender, RoutedEventArgs e)
{
mediaPlayer.PlaybackSession.PlaybackRate = 1.0;
}
Начиная с Windows 10 версии 1803, можно задать поворот видео в MediaPlayer с шагом 90 градусов.
mediaPlayer.PlaybackSession.PlaybackRotation = MediaRotation.Clockwise90Degrees;
Обнаружение ожидаемой и неожиданной буферизации
Объект MediaPlaybackSession, описанный в предыдущем разделе, предоставляет два события для обнаружения начала и окончания буферизации текущего воспроизводимого медиафайла: Буферизация Началась и Буферизация Завершилась. Это позволяет обновить пользовательский интерфейс, чтобы показать пользователю, что происходит буферизация. Начальная буферизация ожидается, когда файл мультимедиа открывается или когда пользователь переключается на новый элемент в списке воспроизведения. Неожиданная буферизация может возникнуть, когда скорость сети снижается или когда у системы управления содержимым возникают технические проблемы. Вы можете использовать событие BufferingStarted для определения, ожидается ли событие буферизации или оно является неожиданным и прерывает воспроизведение. Эти сведения можно использовать в качестве данных телеметрии для приложения или службы доставки мультимедиа.
Регистрируйте обработчики событий BufferingStarted и BufferingEnded для получения уведомлений о состоянии буферизации.
mediaPlayer.PlaybackSession.BufferingStarted += MediaPlaybackSession_BufferingStarted;
mediaPlayer.PlaybackSession.BufferingEnded += MediaPlaybackSession_BufferingEnded;
В обработчике событий BufferingStarted выполните преобразование аргументов события в объект MediaPlaybackSessionBufferingStartedEventArgs и проверьте свойство IsPlaybackInterruption. Если это значение истинно, буферизация, вызвавшая событие, является неожиданной и прерывает воспроизведение. В противном случае ожидается начальная буферизация.
private void MediaPlaybackSession_BufferingStarted(MediaPlaybackSession sender, object args)
{
MediaPlaybackSessionBufferingStartedEventArgs bufferingStartedEventArgs = args as MediaPlaybackSessionBufferingStartedEventArgs;
if (bufferingStartedEventArgs != null && bufferingStartedEventArgs.IsPlaybackInterruption)
{
// update the playback quality telemetry report to indicate that
// playback was interrupted
}
// update the UI to indicate that playback is buffering
}
private void MediaPlaybackSession_BufferingEnded(MediaPlaybackSession sender, object args)
{
// update the UI to indicate that playback is no longer buffering
}
Сведение и масштабирование видео
MediaPlayer позволяет указать исходный прямоугольник в видеоконтенте, который должен отображаться, эффективно позволяя масштабировать видео. Заданный прямоугольник относительно нормализованного прямоугольника (0,0, 1,1), где 0,0 — верхний левый угол кадра, а 1,1 указывает полную ширину и высоту кадра. Таким образом, например, чтобы установить прямоугольник масштабирования так, чтобы отображался правый верхний квадрант видео, можно указать прямоугольник (.5,0,.5,.5). Важно проверить значения, чтобы убедиться, что исходный прямоугольник находится в пределах нормализованного прямоугольника (0,0,1,1). Попытка задать значение за пределами этого диапазона приведет к возникновению исключения.
Чтобы реализовать приближение и масштабирование с помощью многопальцевых жестов, сначала укажите, какие жесты поддерживать. В этом примере запрашиваются жесты масштабирования и перемещения. Событие ManipulationDelta возникает при выполнении одного из зарегистрированных жестов. Событие DoubleTapped будет использоваться для сброса масштаба до полного кадра.
mediaPlayerElement.ManipulationMode = ManipulationModes.Scale | ManipulationModes.TranslateX | ManipulationModes.TranslateY;
mediaPlayerElement.ManipulationDelta += MediaPlayerElement_ManipulationDelta;
mediaPlayerElement.DoubleTapped += MediaPlayerElement_DoubleTapped;
Затем объявите объект Rect , который будет хранить текущий прямоугольник источника масштабирования.
Rect sourceRect = new Rect(0, 0, 1, 1);
Обработчик ManipulationDelta настраивает масштаб или преобразование прямоугольника масштабирования. Если показатель дельта-масштаба не равен 1, это означает, что пользователь выполнил жест сведения. Если значение больше 1, исходный прямоугольник должен быть меньше, чтобы увеличить содержимое. Если значение меньше 1, исходный прямоугольник должен быть увеличен, чтобы уменьшить масштаб. Перед установкой новых значений шкалы результирующий прямоугольник проверяется, чтобы убедиться, что он находится полностью в пределах ограничений (0,0 1,1).
Если значение шкалы равно 1, то обрабатывается жест перевода. Прямоугольник просто смещается на количество пикселей, участвующих в жесте, разделенное на ширину и высоту элемента управления. Опять же, результирующий прямоугольник проверяется, чтобы убедиться, что он находится в пределах границ (0,0 1,1).
Наконец, Параметр NormalizedSourceRect элемента MediaPlaybackSession устанавливается на только что настроенный прямоугольник, указав область в кадре видео, которая должна быть отрисована.
private void MediaPlayerElement_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
if (e.Delta.Scale != 1)
{
var halfWidth = sourceRect.Width / 2;
var halfHeight = sourceRect.Height / 2;
var centerX = sourceRect.X + halfWidth;
var centerY = sourceRect.Y + halfHeight;
var scale = e.Delta.Scale;
var newHalfWidth = (sourceRect.Width * e.Delta.Scale) / 2;
var newHalfHeight = (sourceRect.Height * e.Delta.Scale) / 2;
if (centerX - newHalfWidth > 0 && centerX + newHalfWidth <= 1.0 &&
centerY - newHalfHeight > 0 && centerY + newHalfHeight <= 1.0)
{
sourceRect.X = centerX - newHalfWidth;
sourceRect.Y = centerY - newHalfHeight;
sourceRect.Width *= e.Delta.Scale;
sourceRect.Height *= e.Delta.Scale;
}
}
else
{
var translateX = -1 * e.Delta.Translation.X / mediaPlayerElement.ActualWidth;
var translateY = -1 * e.Delta.Translation.Y / mediaPlayerElement.ActualHeight;
if (sourceRect.X + translateX >= 0 && sourceRect.X + sourceRect.Width + translateX <= 1.0 &&
sourceRect.Y + translateY >= 0 && sourceRect.Y + sourceRect.Height + translateY <= 1.0)
{
sourceRect.X += translateX;
sourceRect.Y += translateY;
}
}
mediaPlayer.PlaybackSession.NormalizedSourceRect = sourceRect;
}
В обработчике событий DoubleTapped исходный прямоугольник устанавливается назад в (0,0,1,1), чтобы обеспечивать рендеринг всего видеокадра.
private void MediaPlayerElement_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{
sourceRect = new Rect(0, 0, 1, 1);
mediaPlayer.PlaybackSession.NormalizedSourceRect = sourceRect;
}
ПРИМЕЧАНИЕ В этом разделе описывается сенсорный ввод. Touchpad отправляет события указателя и не отправляет события манипуляции.
Обработка снижения воспроизведения на основе политик
В некоторых случаях система может ухудшить качество воспроизведения мультимедийного элемента, например, снижение разрешения (ограничение), на основе политики, а не проблемы производительности. Качество видео может ухудшиться, если оно воспроизводится с использованием неподписанного видеодрайвера. Вы можете вызвать MediaPlaybackSession.GetOutputDegradationPolicyState, чтобы определить, происходит ли это снижение на основе политик и почему, для оповещения пользователя или записи причины для целей телеметрии.
В следующем примере показана реализация обработчика для события MediaPlayer.MediaOpened , возникающего при открытии проигрывателя нового элемента мультимедиа. GetOutputDegradationPolicyState вызывается для MediaPlayer, который передан в обработчик. Значение VideoConstrictionReason указывает на причину политики, из-за которой видео сжимается. Если значение не равно None, в этом примере регистрируется причина снижения для целей телеметрии. В этом примере также показано, как задать скорость воспроизведения AdaptiveMediaSource в настоящее время на наименьшую пропускную способность, чтобы сохранить использование данных, так как видео сужается и не будет отображаться при высоком разрешении в любом случае. Дополнительные сведения об использовании AdaptiveMediaSource см. в разделе "Адаптивная потоковая передача".
private void MediaPlayerControl_MediaOpened(MediaPlayer sender, object args)
{
MediaPlaybackSessionOutputDegradationPolicyState info = sender.PlaybackSession.GetOutputDegradationPolicyState();
if (info.VideoConstrictionReason != MediaPlaybackSessionVideoConstrictionReason.None)
{
// Switch to lowest bitrate to save bandwidth
adaptiveMediaSource.DesiredMaxBitrate = adaptiveMediaSource.AvailableBitrates[0];
// Log the degradation reason or show a message to the user
System.Diagnostics.Debug.WriteLine("Logging constriction reason: " + info.VideoConstrictionReason);
}
}
Используйте MediaTimelineController для синхронизации содержимого между несколькими игроками.
Как упоминалось ранее в этой статье, ваше приложение может иметь несколько объектов MediaPlayer , активных одновременно. По умолчанию каждый создаваемый mediaPlayer работает независимо. В некоторых сценариях, таких как синхронизация дорожки комментариев с видео, может потребоваться синхронизировать состояние проигрывателей, позицию воспроизведения и скорость воспроизведения нескольких проигрывателей. Начиная с Windows 10 версии 1607, вы можете реализовать это поведение с помощью класса MediaTimelineController .
Реализация элементов управления воспроизведением
В следующем примере показано, как использовать MediaTimelineController для управления двумя экземплярами MediaPlayer. Во-первых, каждый экземпляр MediaPlayer создается, и источник устанавливается в файл мультимедиа. Далее создается новый MediaTimelineController . Для каждого MediaPlayer отключение связанного с ним mediaPlaybackCommandManager происходит заданием свойству IsEnabled значения false. Затем свойство TimelineController устанавливается в объект контроллера временной шкалы.
MediaTimelineController mediaTimelineController;
mediaPlayer = new MediaPlayer();
mediaPlayer.Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video.mkv"));
mediaPlayerElement.SetMediaPlayer(mediaPlayer);
mediaPlayer2 = new MediaPlayer();
mediaPlayer2.Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video_2.mkv"));
mediaPlayerElement2.SetMediaPlayer(mediaPlayer2);
mediaTimelineController = new MediaTimelineController();
mediaPlayer.CommandManager.IsEnabled = false;
mediaPlayer.TimelineController = mediaTimelineController;
mediaPlayer2.CommandManager.IsEnabled = false;
mediaPlayer2.TimelineController = mediaTimelineController;
ОсторожностьюMediaPlaybackCommandManager обеспечивает автоматическую интеграцию между MediaPlayer и системными элементами управления транспортировкой мультимедиа (SMTC), но эта автоматическая интеграция не может использоваться с проигрывателями мультимедиа, контролируемыми с помощью MediaTimelineController. Поэтому перед настройкой контроллера временной шкалы необходимо отключить диспетчер команд для проигрывателя. Сбой этого приведет к возникновению исключения со следующим сообщением: "Присоединение контроллера временной шкалы мультимедиа блокируется из-за текущего состояния объекта". Дополнительные сведения об интеграции проигрывателя мультимедиа с SMTC см. в разделе "Интеграция с системными элементами управления транспортировкой мультимедиа". Если вы используете MediaTimelineController , вы по-прежнему можете управлять SMTC вручную. Дополнительные сведения см. в разделе "Управление транспортировкой системных носителей".
После подключения MediaTimelineController к одному или нескольким проигрывателям мультимедиа можно управлять состоянием воспроизведения с помощью методов, предоставляемых контроллером. В следующем примере вызывается Start для начала воспроизведения всех связанных медиапроигрывателей с начала мультимедиа.
private void PlayButton_Click(object sender, RoutedEventArgs e)
{
mediaTimelineController.Start();
}
В этом примере показан приостановка и возобновление работы всех подключенных проигрывателей мультимедиа.
private void PauseButton_Click(object sender, RoutedEventArgs e)
{
if (mediaTimelineController.State == MediaTimelineControllerState.Running)
{
mediaTimelineController.Pause();
PauseButton.Content = "Resume";
}
else
{
mediaTimelineController.Resume();
PauseButton.Content = "Pause";
}
}
Чтобы быстро перенаправить все подключенные проигрыватели мультимедиа, задайте скорость воспроизведения значением больше 1.
private void FastForwardButton_Click(object sender, RoutedEventArgs e)
{
mediaTimelineController.ClockRate = 2.0;
}
В следующем примере показано, как использовать элемент управления Slider для отображения текущей позиции воспроизведения контроллера временной шкалы относительно длительности содержимого одного из подключенных проигрывателей мультимедиа. Во-первых, создается новый MediaSource, и регистрируется обработчик для события OpenOperationCompleted источника мультимедиа.
var mediaSource = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video.mkv"));
mediaSource.OpenOperationCompleted += mediaSource_OpenOperationCompleted;
mediaPlayer.Source = mediaSource;
mediaPlayerElement.SetMediaPlayer(mediaPlayer);
Обработчик OpenOperationCompleted используется как возможность обнаружить длительность исходного содержимого носителя. После определения длительности максимальное значение элемента управления Ползунка устанавливается на общее количество секунд элемента мультимедиа. Значение задается внутри вызова DispatcherQueue.TryEnqueue , чтобы убедиться, что он выполняется в потоке пользовательского интерфейса.
TimeSpan duration;
private async void mediaSource_OpenOperationCompleted(MediaSource sender, MediaSourceOpenOperationCompletedEventArgs args)
{
duration = sender.Duration.GetValueOrDefault();
DispatcherQueue.TryEnqueue(() =>
{
PositionSlider.Minimum = 0;
PositionSlider.Maximum = 1;
PositionSlider.StepFrequency = .01;
});
}
Затем регистрируется обработчик события PositionChanged контроллера временной шкалы. Это вызывается периодически системой, примерно 4 раза в секунду.
mediaTimelineController.PositionChanged += mediaTimelineController_PositionChanged;
В обработчике события PositionChanged значение ползунка обновляется, чтобы отразить текущее положение таймлайн контроллера.
private async void mediaTimelineController_PositionChanged(MediaTimelineController sender, object args)
{
if (duration != TimeSpan.Zero)
{
DispatcherQueue.TryEnqueue(() =>
{
PositionSlider.Value = sender.Position.TotalSeconds / (float)duration.TotalSeconds;
});
}
}
Смещайте позицию воспроизведения относительно позиции на временной шкале
В некоторых случаях может потребоваться изменение позиции воспроизведения одного или нескольких медиаплееров, связанных с контроллером таймлайна, относительно других плееров. Это можно сделать, задав свойство TimelineControllerPositionOffset у объекта MediaPlayer, которому требуется смещение. В следующем примере длительность содержимого двух медиаплееров используется для установки минимального и максимального значений двух ползунков элемента управления с учетом длины элемента — плюс и минус.
TimelineOffsetSlider1.Minimum = -1 * duration.TotalSeconds;
TimelineOffsetSlider1.Maximum = duration.TotalSeconds;
TimelineOffsetSlider1.StepFrequency = 1;
TimelineOffsetSlider2.Minimum = -1 * duration2.TotalSeconds;
TimelineOffsetSlider2.Maximum = duration2.TotalSeconds;
TimelineOffsetSlider2.StepFrequency = 1;
В событии ValueChanged для каждого ползунка параметр TimelineControllerPositionOffset для каждого проигрывателя устанавливается в соответствующее значение.
private void TimelineOffsetSlider1_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
{
mediaPlayer.TimelineControllerPositionOffset = TimeSpan.FromSeconds(TimelineOffsetSlider1.Value);
}
private void TimelineOffsetSlider2_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
{
mediaPlayer2.TimelineControllerPositionOffset = TimeSpan.FromSeconds(TimelineOffsetSlider2.Value);
}
Обратите внимание, что если значение смещения проигрывателя сопоставляется с отрицательной позицией воспроизведения, клип останется приостановленным, пока смещение не достигнет нуля, а затем начнется воспроизведение. Аналогичным образом, если значение смещения сопоставляется с положением воспроизведения, превышающим длительность элемента мультимедиа, будет отображаться окончательный кадр, как и когда один проигрыватель мультимедиа достиг конца его содержимого.
Воспроизведение сферического видео с помощью MediaPlayer
Начиная с Windows 10 версии 1703, MediaPlayer поддерживает эквирекангуларную проекцию для сферического просмотра видео. Сферическое видеоконтентное содержимое не отличается от обычного, плоского видео в том, что MediaPlayer будет отображать видео до тех пор, пока кодирование видео поддерживается. Для сферического видео, содержащего тег метаданных, указывающий, что в видео используется эквиректангулярная проекция, MediaPlayer может отображать видео с помощью указанного угла обзора и ориентации просмотра. Это позволяет таким сценариям, как воспроизведение видео в виртуальной реальности с помощью подключенного к голове дисплея или просто позволяет пользователю перемещаться в сферическом видеоконтенте с помощью ввода мыши или клавиатуры.
Чтобы воспроизвести сферическое видео, выполните действия по воспроизведению видеоконтента, описанного ранее в этой статье. Одним из дополнительных шагов является регистрация обработчика для события MediaPlayer.MediaOpened . Это событие дает возможность включить и управлять параметрами воспроизведения сферических видео.
mediaPlayer = new MediaPlayer();
mediaPlayer.MediaOpened += mediaPlayer_MediaOpened;
mediaPlayer.Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video_spherical.mp4"));
mediaPlayerElement.SetMediaPlayer(mediaPlayer);
mediaPlayer.Play();
В обработчике MediaOpened сначала проверьте формат кадра только что открытого элемента мультимедиа, проверив свойство PlaybackSession.SphericalVideoProjection.FrameFormat . Если это значение равно SphericaVideoFrameFormat.Equirectangular, система может автоматически проецировать видеоматериал. Сначала задайте для свойства PlaybackSession.SphericalVideoProjection.IsEnabledзначение true. Вы также можете настроить такие свойства, как ориентация представления и поле представления, которое проигрыватель мультимедиа будет использовать для проецировать видеоконтент. В этом примере поле представления имеет широкое значение 120 градусов, задав свойство HorizontalFieldOfViewInDegrees .
Если содержимое видео является сферическим, но в формате, отличном от equirectangular, можно реализовать собственный алгоритм проекции с помощью режима сервера кадра проигрывателя мультимедиа для получения и обработки отдельных кадров.
private void mediaPlayer_MediaOpened(MediaPlayer sender, object args)
{
if (sender.PlaybackSession.SphericalVideoProjection.FrameFormat == SphericalVideoFrameFormat.Equirectangular)
{
sender.PlaybackSession.SphericalVideoProjection.IsEnabled = true;
sender.PlaybackSession.SphericalVideoProjection.HorizontalFieldOfViewInDegrees = 120;
}
else if (sender.PlaybackSession.SphericalVideoProjection.FrameFormat == SphericalVideoFrameFormat.Unsupported)
{
// If the spherical format is unsupported, you can use frame server mode to implement a custom projection
}
}
В следующем примере кода показано, как при помощи клавиш со стрелками влево и вправо настроить ориентацию сферического видео.
private void RootGrid_KeyDown(object sender, KeyRoutedEventArgs e)
{
if (mediaPlayer.PlaybackSession.SphericalVideoProjection.FrameFormat != SphericalVideoFrameFormat.Equirectangular)
{
return;
}
switch (e.Key)
{
case Windows.System.VirtualKey.Right:
mediaPlayer.PlaybackSession.SphericalVideoProjection.ViewOrientation *= Quaternion.CreateFromYawPitchRoll(.1f, 0, 0);
break;
case Windows.System.VirtualKey.Left:
mediaPlayer.PlaybackSession.SphericalVideoProjection.ViewOrientation *= Quaternion.CreateFromYawPitchRoll(-.1f, 0, 0);
break;
}
}
Если приложение поддерживает списки воспроизведения видео, может потребоваться определить элементы воспроизведения, содержащие сферическое видео в пользовательском интерфейсе. Списки воспроизведения мультимедиа подробно рассматриваются в статье, элементы мультимедиа, списки воспроизведения и треки. В следующем примере показано создание нового списка воспроизведения, добавление элемента и регистрация обработчика для события MediaPlaybackItem.VideoTracksChanged , которое возникает при разрешении видео треков для элемента мультимедиа.
var playbackList = new MediaPlaybackList();
var item = new MediaPlaybackItem(MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/RIFTCOASTER HD_injected.mp4")));
item.VideoTracksChanged += Item_VideoTracksChanged;
playbackList.Items.Add(item);
mediaPlayer.Source = playbackList;
В обработчике событий VideoTracksChanged получите свойства кодирования для всех добавленных видео-треков, вызвав VideoTrack.GetEncodingProperties. Если свойство SphericalVideoFrameFormat свойств кодирования является значением, отличном от SphericaVideoFrameFormat.None, видео-дорожка содержит сферическое видео, и вы можете обновить пользовательский интерфейс соответствующим образом, если вы выберете.
private void Item_VideoTracksChanged(MediaPlaybackItem sender, IVectorChangedEventArgs args)
{
if (args.CollectionChange != CollectionChange.ItemInserted)
{
return;
}
foreach (var videoTrack in sender.VideoTracks)
{
if (videoTrack.GetEncodingProperties().SphericalVideoFrameFormat != SphericalVideoFrameFormat.None)
{
// Optionally indicate in the UI that this item contains spherical video
}
}
}
Использование MediaPlayer в режиме сервера кадров
В режиме сервера кадров MediaPlayer не выполняет автоматическую отрисовку кадров в связанный MediaPlayerElement. Вместо этого приложение копирует текущий кадр из MediaPlayer в объект, реализующий IDirect3DSurface. Основной сценарий этой функции позволяет использовать шейдеры пикселей для обработки видеокадров, предоставляемых MediaPlayer. Ваше приложение отвечает за отображение каждого кадра после обработки, например отображение кадра в элементе управления изображением XAML.
В следующем примере новый mediaPlayer инициализируется, а видеоконтент загружается. Затем регистрируется обработчик для VideoFrameAvailable. Режим сервера кадров включается, присваивая свойству IsVideoFrameServerEnabled объекта MediaPlayer значение true. Наконец, воспроизведение мультимедиа начинается с вызова Play.
mediaPlayer = new MediaPlayer();
mediaPlayer.Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video.mkv"));
mediaPlayer.VideoFrameAvailable += mediaPlayer_VideoFrameAvailable;
mediaPlayer.IsVideoFrameServerEnabled = true;
mediaPlayer.Play();
В следующем примере показан обработчик VideoFrameAvailable, который использует библиотеку Win2D для добавления простого эффекта размытия к каждому кадру видео, а затем отображает обработанные кадры в элементе управления XAML Image. Дополнительные сведения о Win2D см. в разделе "Обзор Win2D".
Всякий раз, когда вызывается обработчик VideoFrameAvailable , метод CopyFrameToVideoSurface используется для копирования содержимого кадра в IDirect3DSurface. Вы также можете использовать CopyFrameToStereoscopicVideoSurfaces для копирования трехмерного содержимого в две поверхности для обработки левого и правого содержимого глаз отдельно. Чтобы получить объект, реализующий IDirect3DSurface , этот пример создает SoftwareBitmap , а затем использует этот объект для создания Объекта Win2D CanvasBitmap, реализующего необходимый интерфейс. CanvasImageSource — это объект Win2D, который можно использовать в качестве источника для изображения. Поэтому создается новый объект, который и устанавливается в качестве источника для изображения, в котором будет отображаться содержимое. Затем создается canvasDrawingSession . Это используется в Win2D для создания эффекта размытия.
После создания экземпляров всех необходимых объектов вызывается CopyFrameToVideoSurface , который копирует текущий кадр из MediaPlayer в CanvasBitmap. Затем создается Win2D GaussianBlurEffect, при этом CanvasBitmap устанавливается в качестве источника операции. Наконец, вызывается CanvasDrawingSession.DrawImage для рисования исходного изображения с уже примененным эффектом размытия в CanvasImageSource, который был связан с элементом управления Image, что позволяет отобразить его в пользовательском интерфейсе.
private async void mediaPlayer_VideoFrameAvailable(MediaPlayer sender, object args)
{
Microsoft.Graphics.Canvas.CanvasDevice canvasDevice = Microsoft.Graphics.Canvas.CanvasDevice.GetSharedDevice();
DispatcherQueue.TryEnqueue(() =>
{
if (frameServerDest == null)
{
// FrameServerImage in this example is a XAML image control
frameServerDest = new SoftwareBitmap(BitmapPixelFormat.Rgba8, (int)FrameServerImage.Width, (int)FrameServerImage.Height, BitmapAlphaMode.Ignore);
}
if (canvasImageSource == null)
{
IntPtr hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
var dpi = GetDpiForWindow(hWnd);
canvasImageSource = new Microsoft.Graphics.Canvas.UI.Xaml.CanvasImageSource(
canvasDevice,
(int)FrameServerImage.Width,
(int)FrameServerImage.Height,
dpi);
FrameServerImage.Source = canvasImageSource;
}
using (Microsoft.Graphics.Canvas.CanvasBitmap inputBitmap =
Microsoft.Graphics.Canvas.CanvasBitmap.CreateFromSoftwareBitmap(canvasDevice, frameServerDest))
{
using (Microsoft.Graphics.Canvas.CanvasDrawingSession ds = canvasImageSource.CreateDrawingSession(Windows.UI.Color.FromArgb(1, 0, 0, 0)))
{
mediaPlayer.CopyFrameToVideoSurface(inputBitmap);
var gaussianBlurEffect = new Microsoft.Graphics.Canvas.Effects.GaussianBlurEffect
{
Source = inputBitmap,
BlurAmount = 5f,
Optimization = Microsoft.Graphics.Canvas.Effects.EffectOptimization.Speed
};
ds.DrawImage(gaussianBlurEffect);
}
}
});
}
Чтобы попробовать пример кода, показанный выше, необходимо добавить пакет NuGet Win2D в проект, выполнив следующие инструкции.
Добавление пакета NuGet Win2D в проект с эффектами
- В обозревателе решений щелкните проект правой кнопкой мыши и выберите пункт "Управление пакетами NuGet".
- В верхней части окна выберите вкладку "Обзор ".
- В поле поиска введите Win2D.
- Выберите Microsoft.Graphics.Win2D и выберите "Установить " в правой области.
- В диалоговом окне "Просмотр изменений" отображается пакет для установки. Нажмите кнопку ОК.
- Примите лицензию на пакет.
Обнаружение и реагирование на изменения уровня звука системой
Ваше приложение может определить, когда система снижает или отключает уровень звука текущего воспроизведения MediaPlayer. Например, система может снизить или "приглушить" уровень воспроизведения звука при срабатывании сигнализации. Система приглушит ваше приложение, когда оно переходит в фоновый режим, если ваше приложение не объявило возможность backgroundMediaPlayback в манифесте приложения. Класс AudioStateMonitor позволяет зарегистрировать событие, когда система изменяет громкость звукового потока. Перейдите к свойству AudioStateMonitormediaPlayer и зарегистрируйте обработчик события SoundLevelChanged , чтобы получать уведомления, когда уровень звука для этого MediaPlayer изменяется системой.
mediaPlayer.AudioStateMonitor.SoundLevelChanged += AudioStateMonitor_SoundLevelChanged;
При обработке события SoundLevelChanged можно выполнять различные действия в зависимости от типа воспроизводимого содержимого. Если вы в данный момент слушаете музыку, то вы можете позволить ей продолжать играть, пока громкость снижается. Если вы слушаете подкаст, то, скорее всего, вы хотите приостановить воспроизведение, когда звук приглушается, чтобы пользователь не пропустил никакого контента.
В этом примере объявляется переменная для отслеживания того, является ли текущее воспроизведение содержимого подкастом. Предполагается, что при выборе содержимого для MediaPlayer нужно задать это значение. Мы также создадим переменную класса для отслеживания программной приостановки воспроизведения при изменении уровня звука.
bool isPodcast;
bool isPausedDueToAudioStateMonitor;
В обработчике событий SoundLevelChanged проверьте свойство SoundLevel отправителя AudioStateMonitor , чтобы определить новый уровень звука. В этом примере проверяется, является ли новый уровень звука максимальным, то есть система перестала выключать звук или уменьшать его, или если уровень звука был снижен, но воспроизводится контент, не относящийся к подкастам. Если одно из этих значений имеет значение true, и содержимое было ранее приостановлено программным способом, воспроизведение возобновляется. Если новый уровень звука отключен или если текущее содержимое является подкастом, а уровень звука низкий, воспроизведение приостановлено, а переменная настроена для отслеживания того, что пауза была инициирована программным способом.
private void AudioStateMonitor_SoundLevelChanged(Windows.Media.Audio.AudioStateMonitor sender, object args)
{
if ((sender.SoundLevel == SoundLevel.Full) || (sender.SoundLevel == SoundLevel.Low && !isPodcast))
{
if (isPausedDueToAudioStateMonitor)
{
mediaPlayer.Play();
isPausedDueToAudioStateMonitor = false;
}
}
else if ((sender.SoundLevel == SoundLevel.Muted) ||
(sender.SoundLevel == SoundLevel.Low && isPodcast))
{
if (mediaPlayer.PlaybackSession.PlaybackState == MediaPlaybackState.Playing)
{
mediaPlayer.Pause();
isPausedDueToAudioStateMonitor = true;
}
}
}
Пользователь может решить приостановить или продолжить воспроизведение, даже если звук приглушён системой. В этом примере показаны обработчики событий для воспроизведения и кнопки приостановки. Обработчик нажатия кнопки приостановки приостанавливает процесс, если воспроизведение уже было приостановлено программным способом; затем мы обновляем переменную, чтобы указать, что пользователь приостановил содержимое. В обработчике нажатия кнопки воспроизведения мы возобновляем воспроизведение и очищаем переменную отслеживания.
private void PauseButton_User_Click(object sender, RoutedEventArgs e)
{
if (isPausedDueToAudioStateMonitor)
{
isPausedDueToAudioStateMonitor = false;
}
else
{
mediaPlayer.Pause();
}
}
public void PlayButton_User_Click(object sender, RoutedEventArgs e)
{
isPausedDueToAudioStateMonitor = false;
mediaPlayer.Play();
}
Связанные темы
Windows developer