建立、排程與管理媒體中斷

本文說明如何建立、排程和管理媒體播放應用程式的媒體中斷。 媒體中斷通常用來將音訊或視訊廣告插入媒體內容。 從 Windows 10 版本 1607 開始,你可以使用 MediaBreakManager 類別快速輕鬆地將媒體中斷新增至任何使用 MediaPlayer 播放的 MediaPlaybackItem

排程一或多個媒體中斷之後,系統會在播放期間於指定的時間自動播放您的媒體內容。 MediaBreakManager 提供事件,讓您的應用程式可以在媒體中斷開始、結束或使用者略過時做出反應。 您也可以存取媒體中斷的 MediaPlaybackSession,以監視下載和緩衝進度更新等事件。

排程媒體中斷

每個 MediaPlaybackItem 物件都有自己的 MediaBreakSchedule,可用來設定播放項目時播放的媒體中斷。 在應用程式中使用媒體中斷的第一個步驟是為您的主要播放內容建立 MediaPlaybackItem

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

有關使用 MediaPlaybackItemMediaPlaybackList 和其他基本媒體播放 API 的更多資訊,請參閱媒體項目、播放清單和軌道

下一個範例示範如何將前插中斷新增至 MediaPlaybackItem,這表示系統會在播放中斷所屬的播放項目之前播放媒體中斷。 首先具現化一個新的 MediaBreak 物件。 在此範例中,會使用 MediaBreakInsertionMethod.Interrupt 呼叫建構函式,這表示在播放中斷內容時,主要內容將會暫停。

接下來,會針對在中斷期間播放的內容建立新的 MediaPlaybackItem,例如廣告。 此播放項目的 CanSkip 屬性設定為 false。 這表示使用者將無法使用內建媒體控制項略過項目。 您的應用程式仍然可以藉由呼叫 SkipCurrentBreak,以程式設計方式略過新增。

媒體中斷的 PlaybackList 屬性是一個 MediaPlaybackList,它讓您可以將多個媒體項目做為播放清單進行播放。 從清單的 Items 集合中新增一個或多個 MediaPlaybackItem 物件,以將它們包含在媒體中斷的播放清單中。

最後,使用主要內容播放項目的 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 以自動開始播放。 將 MediaPlayerSource 屬性設定為主要內容播放項目。 這不是必需的,但您可以將 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 插入方法,這表示系統將在播放廣告時繼續處理主要內容。 這個選項通常由即時串流媒體應用程式使用,而您不希望內容在播放廣告時暫停並落後於即時串流。

此範例也使用接受兩個 TimeSpan 參數的 MediaPlaybackItem 建構函數的多載。 第一個參數會指定媒體中斷項目內開始播放的起點。 第二個參數會指定播放媒體中斷項目的持續時間。 因此,在下列範例中,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);

略過媒體中斷

如同本文前面所提到的,可以設定 MediaPlaybackItemCanSkip 屬性來防止使用者使用內建控制項略過內容。 不過,您可以隨時從程式碼呼叫 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。 您可能想要更新 UI,讓使用者知道正在播放媒體中斷內容。 此範例使用傳入處理常式的 MediaBreakStartedEventArgs 來取得已啟動的媒體中斷的參考。 然後,CurrentItemIndex 屬性用於確定正在播放媒體中斷的播放清單中的媒體項目。 然後,UI 會更新以顯示使用者目前的廣告索引和中斷中剩餘的廣告數目。 請記住,對 UI 的更新必須在 UI 執行緒上進行,因此應在對 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。 您可以使用此事件的處理常式來更新UI,以指出媒體中斷內容不再播放。

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

當使用者在播放 CanSkip 為 true 的項目期間按下內建 UI 中的 Next 按鈕時,或當您透過呼叫 SkipCurrentBreak 略過程式碼中的中斷時,將引發 BreakSkipped 事件。

以下範例使用 MediaPlayerSource 屬性來取得主要內容的媒體項目的參考。 略過的媒體中斷屬於這個項目的中斷排程。 接下來,程式碼檢查略過的媒體中斷是否與排程的 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 分鐘。 如果是這樣,則透過呼叫 MediaPlayer.BreakManagerPlayBreak 方法立即播放從事件參數公開的 SeekedOverBreaks 集合中取得的第一個中斷。

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,您可以存取它來取得與正在播放的媒體中斷內容特別相關的資料和事件。 您可以從播放工作階段取得的資訊包括目前的播放狀態、播放或暫停,以及內容中的目前播放位置。 如果媒體中斷內容的外觀比例與主要內容不同,您可以使用 NaturalVideoWidthNaturalVideoHeight 屬性以及 NaturalVideoSizeChanged 來調整影片 UI。 您也可以接收 BufferingStartedBufferingEndedDownloadProgressChanged 等事件,這些事件可以提供有關應用程式效能的有價值的遙測資料。

以下範例為 BufferingProgressChanged event 事件註冊一個處理常式;在事件處理常式中,它會更新 UI 以顯示目前的緩衝進度。

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