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

В этой статье показано, как создавать и планировать мультимедийные вставки, а также управлять ими в приложении воспроизведения мультимедиа. Мультимедийные вставки обычно используются для вставки аудио- или видеорекламы в мультимедийное содержимое. Начиная с Windows 10 версии 1607 можно использовать класс MediaBreakManager, чтобы легко и быстро добавлять мультимедийные вставки в любой элемент MediaPlaybackItem, воспроизводимый с помощью MediaPlayer.

Когда запланирована одна или несколько мультимедийных вставок, система автоматически воспроизводит ваше мультимедийное содержимое в указанный момент во время воспроизведения. MediaBreakManager предоставляет события, чтобы приложение могло реагировать на начало и завершение мультимедийных вставок при воспроизведении мультимедиа, а также на случаи, когда вставки пропускаются пользователем. Можно также получить доступ к объекту MediaPlaybackSession для ваших мультимедийных вставок, чтобы контролировать такие события, как обновление хода выполнения загрузки и буферизации.

Планирование мультимедийных вставок

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

MediaPlaybackItem moviePlaybackItem =
    new MediaPlaybackItem(MediaSource.CreateFromUri(new Uri("http://www.fabrikam.com/movie.mkv")));

Дополнительные сведения о работе с MediaPlaybackItem, MediaPlaybackList и другими основными API воспроизведения мультимедиа см. в разделе Элементы, списки воспроизведения и звуковые дорожки мультимедиа.

В следующем примере показано, как добавить вставку перед началом воспроизведения в объект MediaPlaybackItem; это означает, что система будет воспроизводить мультимедийную вставку перед воспроизведением элемента воспроизведения, к которому относится вставка. Сначала создается новый экземпляр объекта MediaBreak. В этом примере конструктор вызывается с параметром MediaBreakInsertionMethod.Interrupt; это означает, что во время воспроизведения мультимедийной вставки воспроизведение основного содержимого приостанавливается.

Далее создается новый элемент MediaPlaybackItem для содержимого, которое будет воспроизводиться во время вставки, например рекламы. Для свойства CanSkip этого элемента воспроизведения задается значение false. Это означает, что пользователь не сможет пропустить этот элемент с помощью встроенных элементов управления мультимедиа. Однако приложение все равно может пропустить мультимедийную вставку программным образом, вызвав метод SkipCurrentBreak.

Свойство PlaybackList мультимедийной вставки представляет собой MediaPlaybackList, который позволяет воспроизводить несколько элементов мультимедиа в виде списка воспроизведения. Добавьте один или несколько объектов MediaPlaybackItem из коллекции Items списка, чтобы включить их в список воспроизведения мультимедийной вставки.

Наконец, запланируйте мультимедийную вставку с помощью свойства BreakSchedule элемента воспроизведения основного содержимого. Укажите, что вставка должна воспроизводиться перед началом элемента, назначив ее свойству PrerollBreak объекта расписания.

MediaBreak preRollMediaBreak = new MediaBreak(MediaBreakInsertionMethod.Interrupt);
MediaPlaybackItem prerollAd = 
    new MediaPlaybackItem(MediaSource.CreateFromUri(new Uri("http://www.fabrikam.com/preroll_ad.mp4")));
prerollAd.CanSkip = false;
preRollMediaBreak.PlaybackList.Items.Add(prerollAd);

moviePlaybackItem.BreakSchedule.PrerollBreak = preRollMediaBreak;

Теперь можно воспроизвести основной элемент мультимедиа, и созданная мультимедийная вставка будет воспроизводиться перед основным содержимым. Создайте новый объект MediaPlayer и, если требуется, задайте для свойства AutoPlay значение true, чтобы запускать воспроизведение автоматически. Задайте для свойства Source объекта MediaPlayer элемент воспроизведения основного содержимого. Это необязательно, но вы можете назначить объект MediaPlayer объекту MediaPlayerElement для отображения мультимедиа на странице XAML. Дополнительные сведения об использовании объекта MediaPlayer см. в разделе Воспроизведение аудио и видео с помощью MediaPlayer.

_mediaPlayer = new MediaPlayer();
_mediaPlayer.AutoPlay = true;
_mediaPlayer.Source = moviePlaybackItem;
mediaPlayerElement.SetMediaPlayer(_mediaPlayer);

Вставка после завершения воспроизведения, которая воспроизводится после завершения воспроизведения элемента MediaPlaybackItem, содержащего основное содержимое, добавляется таким же образом, как и вставка перед началом воспроизведения, за исключением того, что объект MediaBreak назначается свойству PostrollBreak.

MediaBreak postrollMediaBreak = new MediaBreak(MediaBreakInsertionMethod.Interrupt);
MediaPlaybackItem postRollAd =
    new MediaPlaybackItem(MediaSource.CreateFromUri(new Uri("http://www.fabrikam.com/postroll_ad.mp4")));
postrollMediaBreak.PlaybackList.Items.Add(postRollAd);

moviePlaybackItem.BreakSchedule.PostrollBreak = postrollMediaBreak;

Можно также запланировать одну или несколько вставок во время воспроизведения, которые воспроизводятся в указанный момент во время воспроизведения основного содержимого. В следующем примере объект MediaBreak создается с помощью перегруженного конструктора, принимающего объект TimeSpan, который указывает время в течение воспроизведения основного элемента мультимедиа, когда будет воспроизводиться вставка. Здесь снова MediaBreakInsertionMethod.Interrupt указывает, что во время воспроизведения вставки воспроизведение основного содержимого будет приостановлено. Вставка во время воспроизведения добавляется в расписание вызовом метода InsertMidrollBreak. Доступный только для чтения текущий список вставок во время воспроизведения можно получить, обратившись к свойству MidrollBreaks.

MediaBreak midrollMediaBreak = new MediaBreak(MediaBreakInsertionMethod.Interrupt, TimeSpan.FromMinutes(10));
midrollMediaBreak.PlaybackList.Items.Add(
    new MediaPlaybackItem(MediaSource.CreateFromUri(new Uri("http://www.fabrikam.com/midroll_ad_1.mp4"))));
midrollMediaBreak.PlaybackList.Items.Add(
    new MediaPlaybackItem(MediaSource.CreateFromUri(new Uri("http://www.fabrikam.com/midroll_ad_2.mp4"))));
moviePlaybackItem.BreakSchedule.InsertMidrollBreak(midrollMediaBreak);

В следующем примере вставки во время воспроизведения показано использование метода вставки MediaBreakInsertionMethod.Replace, который означает, что система будет продолжать обработку основного содержимого во время воспроизведения вставки. Этот вариант обычно используется приложениями прямой потоковой передачи мультимедиа, когда во время рекламы не требуется приостанавливать воспроизведение основного содержимого, чтобы не отставать от прямого эфира.

В этом примере также используется перегруженный конструктор MediaPlaybackItem, который принимает два параметра TimeSpan. Первый параметр указывает начальную точку в элементе мультимедийной вставки, с которой начнется воспроизведение. Второй параметр задает длительность воспроизведения элемента мультимедийной вставки. Таким образом, в следующем примере воспроизведение MediaBreak начнется через 20 минут после начала основного содержимого. Воспроизведение мультимедийного элемента начнется в точке, отстоящей на 30 секунд от начала элемента мультимедийной вставки, и будет длиться 15 секунд, после чего продолжится воспроизведение основного содержимого.

midrollMediaBreak = new MediaBreak(MediaBreakInsertionMethod.Replace, TimeSpan.FromMinutes(20));
MediaPlaybackItem ad = 
    new MediaPlaybackItem(MediaSource.CreateFromUri(new Uri("http://www.fabrikam.com/midroll_ad_3.mp4")),
    TimeSpan.FromSeconds(30),
    TimeSpan.FromSeconds(15));
ad.CanSkip = false;
midrollMediaBreak.PlaybackList.Items.Add(ad);

Пропуск мультимедийных вставок

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

private void SkipButton_Click(object sender, RoutedEventArgs e) => _mediaPlayer.BreakManager.SkipCurrentBreak();

Обработка событий MediaBreak

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

_mediaPlayer.BreakManager.BreakStarted += BreakManager_BreakStarted;
_mediaPlayer.BreakManager.BreakEnded += BreakManager_BreakEnded;
_mediaPlayer.BreakManager.BreakSkipped += BreakManager_BreakSkipped;
_mediaPlayer.BreakManager.BreaksSeekedOver += BreakManager_BreaksSeekedOver;

Событие BreakStarted возникает, когда начинается мультимедийная вставка. Можно обновить пользовательский интерфейс, чтобы пользователь знал, что воспроизводится мультимедийная вставка. В этом примере используется объект MediaBreakStartedEventArgs, передаваемый в обработчик для получения ссылки на начавшуюся мультимедийную вставку. Затем свойство CurrentItemIndex используется для определения воспроизводимого элемента мультимедиа из списка воспроизведения мультимедийной вставки. Далее обновляется пользовательский интерфейс, чтобы показать пользователю индекс текущей рекламы и количество рекламных роликов, оставшихся во вставке. Помните, что обновление пользовательского интерфейса должно выполняться в потоке пользовательского интерфейса, поэтому вызов следует производить из вызова метода RunAsync.

private async void BreakManager_BreakStarted(MediaBreakManager sender, MediaBreakStartedEventArgs args)
{
    MediaBreak currentBreak = sender.CurrentBreak;
    var currentIndex = currentBreak.PlaybackList.CurrentItemIndex;
    var itemCount = currentBreak.PlaybackList.Items.Count;

    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>            
        statusTextBlock.Text = $"Playing ad {currentIndex + 1} of {itemCount}");
}

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

private async void BreakManager_BreakEnded(MediaBreakManager sender, MediaBreakEndedEventArgs args)
{
    // Update UI to show that the MediaBreak is no longer playing
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => statusTextBlock.Text = "");

    args.MediaBreak.CanStart = false;
}

Событие BreakSkipped возникает, когда пользователь нажимает кнопку Далее во встроенном пользовательском интерфейсе во время воспроизведения элемента, для которого свойство CanSkip имеет значение true, или при пропуске вставки из кода путем вызова метода SkipCurrentBreak.

В следующем примере свойство Source объекта MediaPlayer используется для получения ссылки на элемент мультимедиа для основного содержимого. Пропущенная мультимедийная вставка входит в расписание вставок для этого элемента. Далее код проверяет, задано ли пропущенной мультимедийной вставке свойство расписания PrerollBreak. Если да, то была пропущена вставка перед началом воспроизведения; в таком случае создается новая вставка, воспроизведение которой планируется через 10 минут после начала основного содержимого.

private async void BreakManager_BreakSkipped(MediaBreakManager sender, MediaBreakSkippedEventArgs args)
{
    // Update UI to show that the MediaBreak is no longer playing
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => statusTextBlock.Text = "");

    MediaPlaybackItem currentItem = _mediaPlayer.Source as MediaPlaybackItem;
    if(!(currentItem.BreakSchedule.PrerollBreak is  null) 
        && currentItem.BreakSchedule.PrerollBreak == args.MediaBreak)
    {
        MediaBreak mediaBreak = new MediaBreak(MediaBreakInsertionMethod.Interrupt, TimeSpan.FromMinutes(10));
        mediaBreak.PlaybackList.Items.Add(await GetAdPlaybackItem());
        currentItem.BreakSchedule.InsertMidrollBreak(mediaBreak);
    }
}

Событие BreaksSeekedOver возникает, когда при прокрутке положение воспроизведения основного элемента мультимедиа переходит через запланированное время для одной или нескольких мультимедийных вставок. В следующем примере проверяется, не произошел ли переход более чем через одну мультимедийную вставку при прокрутке положения воспроизведения вперед, а также не была ли выполнена прокрутка вперед менее чем на 10 минут. Если эти условия выполнены, первая вставка, через которую был выполнен переход (она получается из коллекции SeekedOverBreaks, предоставленной аргументами события), немедленно воспроизводится с помощью вызова метода PlayBreak объекта MediaPlayer.BreakManager.

private void BreakManager_BreaksSeekedOver(MediaBreakManager sender, MediaBreakSeekedOverEventArgs args)
{
    if(args.SeekedOverBreaks.Count > 1
        && args.NewPosition.TotalMinutes > args.OldPosition.TotalMinutes
        && args.NewPosition.TotalMinutes - args.OldPosition.TotalMinutes < 10.0)
        _mediaPlayer.BreakManager.PlayBreak(args.SeekedOverBreaks[0]);
}

Доступ к текущему сеансу воспроизведения

Объект MediaPlaybackSession использует класс MediaPlayer для предоставления данных и событий, связанных с текущим воспроизводимым мультимедийным содержимым. MediaBreakManager также имеет объект MediaPlaybackSession, к которому можно обращаться для получения данных и событий, связанных с конкретным воспроизводимым содержимым мультимедийной вставки. Сведения, которые можно получить из сеанса воспроизведения, включают в себя текущее состояние воспроизведения (воспроизводится или приостановлено) и текущее положение воспроизведения в рамках содержимого. Свойства NaturalVideoWidth и NaturalVideoHeight, а также NaturalVideoSizeChanged можно использовать для настройки пользовательского интерфейса видео, если мультимедийная вставка и основное содержимое имеют разное соотношение сторон. Можно также получать такие события, как BufferingStarted, BufferingEnded и DownloadProgressChanged, которые предоставляют ценную телеметрии о производительности приложения.

В следующем примере регистрируется обработчик события BufferingProgressChanged; в обработчике события пользовательский интерфейс обновляется для отображения текущего хода выполнения буферизации.

_mediaPlayer.BreakManager.PlaybackSession.BufferingProgressChanged += PlaybackSession_BufferingProgressChanged;
private async void PlaybackSession_BufferingProgressChanged(MediaPlaybackSession sender, object args)
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
        bufferingProgressBar.Value = sender.BufferingProgress);
}