彈性資料流Adaptive streaming

本文說明如何將彈性串流多媒體內容播放新增到通用 Windows 平台 (UWP) 應用程式。This article describes how to add playback of adaptive streaming multimedia content to a Universal Windows Platform (UWP) app. 本功能支援 HTTP 即時串流 (HLS) 與 HTTP 動態串流 (DASH) 內容播放。This feature supports playback of Http Live Streaming (HLS) and Dynamic Streaming over HTTP (DASH) content. 從 Windows 10 版本 1803 開始,AdaptiveMediaSource 支援平滑串流處理。Starting with Windows 10, version 1803, Smooth Streaming is supported by AdaptiveMediaSource.

如需支援的 HLS 通訊協定標記的清單,請參閱 HLS 標記支援For a list of supported HLS protocol tags, see HLS tag support.

如需所支援 DASH 設定檔的清單,請參閱 DASH 設定檔支援For a list of supported DASH profiles, see DASH profile support.

注意

本文中的程式碼是採用 UWP 彈性資料流範例的程式碼。The code in this article was adapted from the UWP Adaptive streaming sample.

使用 MediaPlayer 與 MediaPlayerElement 的簡易彈性資料流 Simple adaptive streaming with MediaPlayer and MediaPlayerElement

若要在 UWP app 中播放彈性資料流媒體,請建立一個指向 DASH 或 HLS 資訊清單檔案的 Uri 物件。To play adaptive streaming media in a UWP app, create a Uri object pointing to a DASH or HLS manifest file. 建立 MediaPlayer 類別的執行個體。Create an instance of the MediaPlayer class. 呼叫 MediaSource.CreateFromUri 以建立新的 MediaSource 物件,然後將它設定為 MediaPlayerSource 屬性。Call MediaSource.CreateFromUri to create a new MediaSource object and then set that to the Source property of the MediaPlayer. 呼叫 Play 以開始播放媒體內容。Call Play to start playback of the media content.

MediaPlayer _mediaPlayer;
System.Uri manifestUri = new Uri("http://amssamples.streaming.mediaservices.windows.net/49b57c87-f5f3-48b3-ba22-c55cfdffa9cb/Sintel.ism/manifest(format=m3u8-aapl)");
_mediaPlayer = new MediaPlayer();
_mediaPlayer.Source = MediaSource.CreateFromUri(manifestUri);
_mediaPlayer.Play();

上述範例會播放媒體內容的音訊,但不會自動轉譯您 UI 中的內容。The above example will play the audio of the media content but it doesn't automatically render the content in your UI. 播放視訊內容的多數應用程式都會想轉譯 XAML 頁面中的內容。Most apps that play video content will want to render the content in a XAML page. 若要這樣做,請新增 MediaPlayerElement 控制項到您的 XAML 頁面。To do this, add a MediaPlayerElement control to your XAML page.

<MediaPlayerElement x:Name="mediaPlayerElement" HorizontalAlignment="Stretch" AreTransportControlsEnabled="True"/>

呼叫 MediaSource.CreateFromUri 以從 DASH 或 HLS 資訊清單檔案的 URI 建立 MediaSourceCall MediaSource.CreateFromUri to create a MediaSource from the URI of a DASH or HLS manifest file. 接著設定 MediaPlayerElementSource 屬性。Then set the Source property of the MediaPlayerElement. MediaPlayerElement 將會自動為內容建立一個新的 MediaPlayer 物件。The MediaPlayerElement will automatically create a new MediaPlayer object for the content. 您可以在 MediaPlayer 上呼叫 Play 以開始播放內容。You can call Play on the MediaPlayer to start playback of the content.

System.Uri manifestUri = new Uri("http://amssamples.streaming.mediaservices.windows.net/49b57c87-f5f3-48b3-ba22-c55cfdffa9cb/Sintel.ism/manifest(format=m3u8-aapl)");
mediaPlayerElement.Source = MediaSource.CreateFromUri(manifestUri);
mediaPlayerElement.MediaPlayer.Play();

注意

從 Windows 10 版本 1607 開始,建議您使用 MediaPlayer 類別來播放媒體項目。Starting with Windows 10, version 1607, it is recommended that you use the MediaPlayer class to play media items. MediaPlayerElement 是輕量型的 XAML 控制項,可用來轉譯 XAML 頁面中的 MediaPlayer 內容。The MediaPlayerElement is a lightweight XAML control that is used to render the content of a MediaPlayer in a XAML page. MediaElement 控制項仍持續受支援,以提供回溯相容性。The MediaElement control continues to be supported for backwards compatibility. 如需使用 MediaPlayerMediaPlayerElement 播放媒體內容的詳細資訊,請參閱使用 MediaPlayer 播放音訊和視訊For more information about using MediaPlayer and MediaPlayerElement to play media content, see Play audio and video with MediaPlayer. 如需使用 MediaSource 和相關 API 來處理媒體內容的詳細資訊,請參閱媒體項目、播放清單和曲目For information about using MediaSource and related APIs to work with media content, see Media items, playlists, and tracks.

使用 AdaptiveMediaSource 的彈性資料流Adaptive streaming with AdaptiveMediaSource

如果您的 app 需要更多進階彈性資料流功能 (例如提供自訂 HTTP 標頭、監視目前下載與播放位元速率,或調整判斷系統切換彈性資料流位元速率時機的比率),請使用 AdaptiveMediaSource 物件。If your app requires more advanced adaptive streaming features, such as providing custom HTTP headers, monitoring the current download and playback bitrates, or adjusting the ratios that determine when the system switches bitrates of the adaptive stream, use the AdaptiveMediaSource object.

可在 Windows.Media.Streaming.Adaptive 命名空間中找到彈性資料流 API。The adaptive streaming APIs are found in the Windows.Media.Streaming.Adaptive namespace. 本文中的範例使用下列命名空間中的 API。The examples in this article use APIs from the following namespaces.

using Windows.Media.Streaming.Adaptive;
using System.Threading.Tasks;
using Windows.Storage.Streams;
using Windows.Media.Playback;
using Windows.Media.Core;

從 URI 初始化 AdaptiveMediaSource。Initialize an AdaptiveMediaSource from a URI.

透過呼叫 CreateFromUriAsync,使用彈性資料流資訊清單檔的 URI 初始化 AdaptiveMediaSourceInitialize the AdaptiveMediaSource with the URI of an adaptive streaming manifest file by calling CreateFromUriAsync. 此方法傳回的 AdaptiveMediaSourceCreationStatus 值會讓您知道是否順利建立媒體來源。The AdaptiveMediaSourceCreationStatus value returned from this method lets you know if the media source was created successfully. 如果是這樣,您可以將物件設定為 MediaPlayer 的串流來源,方式為呼叫 MediaSource.CreateFromAdaptiveMediaSource,再將其指派給媒體播放器的 Source 屬性,以建立 MediaSource 物件。If so, you can set the object as the stream source for your MediaPlayer by creating a MediaSource object by calling MediaSource.CreateFromAdaptiveMediaSource, and then assigning it to the media player's Source property. 在這個範例中,會查詢 AvailableBitrates 屬性來判斷串流的位元速率支援上限,然後將該值設為初始位元速率。In this example, the AvailableBitrates property is queried to determine the maximum supported bitrate for this stream, and then that value is set as the inital bitrate. 這個範例也會暫存本文稍後討論之數個 AdaptiveMediaSource 事件的處理常式。This example also registers handlers for the several AdaptiveMediaSource events that are discussed later in this article.

async private void InitializeAdaptiveMediaSource(System.Uri uri)
{
    AdaptiveMediaSourceCreationResult result = await AdaptiveMediaSource.CreateFromUriAsync(uri);

    if (result.Status == AdaptiveMediaSourceCreationStatus.Success)
    {
        ams = result.MediaSource;
        mediaPlayerElement.SetMediaPlayer(new MediaPlayer());
        mediaPlayerElement.MediaPlayer.Source = MediaSource.CreateFromAdaptiveMediaSource(ams);
        mediaPlayerElement.MediaPlayer.Play();


        ams.InitialBitrate = ams.AvailableBitrates.Max<uint>();

        //Register for download requests
        ams.DownloadRequested += DownloadRequested;

        //Register for download failure and completion events
        ams.DownloadCompleted += DownloadCompleted;
        ams.DownloadFailed += DownloadFailed;

        //Register for bitrate change events
        ams.DownloadBitrateChanged += DownloadBitrateChanged;
        ams.PlaybackBitrateChanged += PlaybackBitrateChanged;

        //Register for diagnostic event
        ams.Diagnostics.DiagnosticAvailable += DiagnosticAvailable;
    }
    else
    {
        // Handle failure to create the adaptive media source
        MyLogMessageFunction($"Adaptive source creation failed: {uri} - {result.ExtendedError}");
    }
}

使用 HttpClient 初始化 AdaptiveMediaSourceInitialize an AdaptiveMediaSource using HttpClient

如果您需要設定自訂的 HTTP 標頭以取得資訊清單檔,您可以建立 HttpClient 物件、設定想要的標題,然後將物件傳遞到 CreateFromUriAsync 的多載中。If you need to set custom HTTP headers for getting the manifest file, you can create an HttpClient object, set the desired headers, and then pass the object into the overload of CreateFromUriAsync.

httpClient = new Windows.Web.Http.HttpClient();
httpClient.DefaultRequestHeaders.TryAppendWithoutValidation("X-CustomHeader", "This is a custom header");
AdaptiveMediaSourceCreationResult result = await AdaptiveMediaSource.CreateFromUriAsync(manifestUri, httpClient);

當系統即將從伺服器擷取資源時,會引發 DownloadRequested 事件。The DownloadRequested event is raised when the system is about to retrieve a resource from the server. 傳遞至事件處理常式的 AdaptiveMediaSourceDownloadRequestedEventArgs 會公開提供要求之資源的相關資訊 (例如資源類型和 URI) 的內容。The AdaptiveMediaSourceDownloadRequestedEventArgs passed into the event handler exposes properties that provide information about the resource being requested such as the type and URI of the resource.

使用 DownloadRequested 事件修改資源要求屬性Modify resource request properties using the DownloadRequested event

您可以使用 DownloadRequested 事件處理常式,透過更新事件引數所提供之 AdaptiveMediaSourceDownloadResult 物件的內容,來修改資源要求。You can use the DownloadRequested event handler to modify the resource request by updating the properties of the AdaptiveMediaSourceDownloadResult object provided by the event args. 在下列範例中,會透過更新結果物件的 ResourceUri 內容來修改要從中擷取資源的 URI。In the example below, the URI from which the resource will be retrieved is modified by updating the ResourceUri properties of the result object. 您也可以重新撰寫媒體區段的位元組範圍偏移和長度,或者,如下列範例所示,變更資源 URI 以下載完整的資源,並將位元組範圍偏移和長度設定為空值。You can also rewrite the byte range offset and length for media segments or, as shown the example below, change the resource URI to download the full resource and set the byte range offset and length to null.

您可以透過設定結果物件的 BufferInputStream 屬性,來覆寫要求之資源的內容。You can override the content of the requested resource by setting the Buffer or InputStream properties of the result object. 在下列範例中,會透過設定 Buffer 屬性來取代資訊清單資源的內容。In the example below, the contents of the manifest resource are replaced by setting the Buffer property. 請注意,如果您是使用非同步取得的資料來更新資源要求 (例如從遠端伺服器或非同步使用者驗證擷取資料),您必須呼叫 AdaptiveMediaSourceDownloadRequestedEventArgs.GetDeferral 取得延遲,然後在作業完成時呼叫 Complete,通知系統可以繼續下載要求作業。Note that if you are updating the resource request with data that is obtained asynchronously, such as retrieving data from a remote server or asynchronous user authentication, you must call AdaptiveMediaSourceDownloadRequestedEventArgs.GetDeferral to get a deferral and then call Complete when the operation is complete to signal the system that the download request operation can continue.

    private async void DownloadRequested(AdaptiveMediaSource sender, AdaptiveMediaSourceDownloadRequestedEventArgs args)
    {

        // rewrite key URIs to replace http:// with https://
        if (args.ResourceType == AdaptiveMediaSourceResourceType.Key)
        {
            string originalUri = args.ResourceUri.ToString();
            string secureUri = originalUri.Replace("http:", "https:");

            // override the URI by setting property on the result sub object
            args.Result.ResourceUri = new Uri(secureUri);
        }

        if (args.ResourceType == AdaptiveMediaSourceResourceType.Manifest)
        {
            AdaptiveMediaSourceDownloadRequestedDeferral deferral = args.GetDeferral();
            args.Result.Buffer = await CreateMyCustomManifest(args.ResourceUri);
            deferral.Complete();
        }

        if (args.ResourceType == AdaptiveMediaSourceResourceType.MediaSegment)
        {
            var resourceUri = args.ResourceUri.ToString() + "?range=" + 
                args.ResourceByteRangeOffset + "-" + (args.ResourceByteRangeLength - 1);

            // override the URI by setting a property on the result sub object
            args.Result.ResourceUri = new Uri(resourceUri);

            // clear the byte range properties on the result sub object
            args.Result.ResourceByteRangeOffset = null;
            args.Result.ResourceByteRangeLength = null;
        }
    }

使用 bitrate 事件管理和回應位元速率變更Use bitrate events to manage and respond to bitrate changes

AdaptiveMediaSource 物件提供了可讓您在下載或播放位元速率變更時用來反應的事件。The AdaptiveMediaSource object provides events that allow you to react when the download or playback bitrates change. 在此範例中,目前的位元速率僅在 UI 中更新。In this example, the current bitrates are simply updated in the UI. 請注意,您可以修改判斷系統切換彈性資料流位元速率時機的比率。Note that you can modify the ratios that determine when the system switches bitrates of the adaptive stream. 如需詳細資訊,請參閱 AdvancedSettings 屬性。For more information, see the AdvancedSettings property.

private async void DownloadBitrateChanged(AdaptiveMediaSource sender, AdaptiveMediaSourceDownloadBitrateChangedEventArgs args)
{
    await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, new DispatchedHandler(() =>
    {
        txtDownloadBitrate.Text = args.NewValue.ToString();
    }));
}

private async void PlaybackBitrateChanged(AdaptiveMediaSource sender, AdaptiveMediaSourcePlaybackBitrateChangedEventArgs args)
{
    await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, new DispatchedHandler(() =>
    {
        txtPlaybackBitrate.Text = args.NewValue.ToString();
    }));
}

處理下載完成和失敗事件Handle download completion and failure events

所要求資源的下載失敗時,AdaptiveMediaSource 物件會引發 DownloadFailed 事件。The AdaptiveMediaSource object raises the DownloadFailed event when the download of a requested resource fails. 您可以使用這個事件更新 UI,以回應失敗。You can use this event to update your UI in response to the failure. 您也可以使用事件來記錄下載作業和失敗的統計資訊。You can also use the event to log statistical information about the download operation and the failure.

傳入事件處理常式的 AdaptiveMediaSourceDownloadFailedEventArgs 物件包含失敗資源下載的中繼資料,例如資源類型、資源 URI,以及資料流內發生失敗的位置。The AdaptiveMediaSourceDownloadFailedEventArgs object passed into the event handler contains metadata about the failed resource download, such as the resource type, the URI of the resource, and the position within the stream where the failure occurred. RequestId 取得要求的系統產生的唯一識別碼,這個要求可用來關聯多個事件中個人要求的狀態資訊。The RequestId gets a system-generated unique identifier for the request that can be use to correlate status information about an individual request across multiple events.

Statistics 屬性傳回 AdaptiveMediaSourceDownloadStatistics 物件,以提供發生事件時所收到的位元組數目,以及下載作業中各種里程碑的時間。The Statistics property returns a AdaptiveMediaSourceDownloadStatistics object which provides detailed information about the number of bytes received at the time of the event and the timing of various milestones in the download operation. 您可以記錄這項資訊,以找出彈性資料流實作的效能問題。You can log this information in order identify perfomance issues in your adaptive streaming implementation.

private void DownloadFailed(AdaptiveMediaSource sender, AdaptiveMediaSourceDownloadFailedEventArgs args)
{
    var statistics = args.Statistics;

    MyLogMessageFunction("download failed for: " + args.ResourceType + 
     " - " + args.ResourceUri +
     " – Error:" + args.ExtendedError.HResult +
     " - RequestId" + args.RequestId + 
     " – Position:" + args.Position +
     " - Duration:" + args.ResourceDuration +
     " - ContentType:" + args.ResourceContentType +
     " - TimeToHeadersReceived:" + statistics.TimeToHeadersReceived + 
     " - TimeToFirstByteReceived:" + statistics.TimeToFirstByteReceived + 
     " - TimeToLastByteReceived:" + statistics.TimeToLastByteReceived +
     " - ContentBytesReceivedCount:" + statistics.ContentBytesReceivedCount);

}

DownloadCompleted 事件是在資源下載完成時發生,並將類似的資料提供給 DownloadFailed 事件。The DownloadCompleted event occurs when a resource download completes and provdes similar data to the DownloadFailed event. 同樣地,提供 RequestId 來關聯單一要求的事件。Once again, a RequestId is provided for correlating events for a single request. 此外,提供 AdaptiveMediaSourceDownloadStatistics 物件來啟用下載統計資料的記錄。Also, an AdaptiveMediaSourceDownloadStatistics object is provided to enable logging of download stats.

private void DownloadCompleted(AdaptiveMediaSource sender, AdaptiveMediaSourceDownloadCompletedEventArgs args)
{
    var statistics = args.Statistics;

    MyLogMessageFunction("download completed for: " + args.ResourceType + " - " +
     args.ResourceUri +
     " – RequestId:" + args.RequestId +
     " – Position:" + args.Position +
     " - Duration:" + args.ResourceDuration +
     " - ContentType:" + args.ResourceContentType +
     " - TimeToHeadersReceived:" + statistics.TimeToHeadersReceived + 
     " - TimeToFirstByteReceived:" + statistics.TimeToFirstByteReceived + 
     " - TimeToLastByteReceived:" + statistics.TimeToLastByteReceived +
     " - ContentBytesReceivedCount:" + statistics.ContentBytesReceivedCount);

}

使用 AdaptiveMediaSourceDiagnostics 收集彈性資料流遙測資料Gather adaptive streaming telemetry data with AdaptiveMediaSourceDiagnostics

AdaptiveMediaSource 公開 Diagnostics 屬性,以傳回 AdaptiveMediaSourceDiagnostics物件。The AdaptiveMediaSource exposes a Diagnostics property which returns an AdaptiveMediaSourceDiagnostics object. 使用這個物件註冊 DiagnosticAvailable 事件。Use this object to register for the DiagnosticAvailable event. 這個事件適用於遙測收集,不應該用來修改執行階段的應用程式行為。This event is intended to be used for telemetry collection and should not be used to modify app behavior at runtime. 有許多不同的原因會引發這個診斷事件。This diagnostic event is raised for many different reasons. 檢查傳入事件的 AdaptiveMediaSourceDiagnosticAvailableEventArgs 物件的 DiagnosticType 屬性,以判斷引發事件的原因。Check the DiagnosticType property of the AdaptiveMediaSourceDiagnosticAvailableEventArgs object passed into the event to determine the reason that the event was raised. 可能的原因包含存取所要求資源的錯誤以及剖析資料流資訊清單檔案的錯誤。Potential reasons include errors accessing the requested resource and errors parsing the streaming manifest file. 如需可觸發診斷事件的情況清單,請參閱 AdaptiveMediaSourceDiagnosticTypeFor a list of situations that can trigger a diagnostic event, see AdaptiveMediaSourceDiagnosticType. 與其他彈性資料流事件的引數一樣,AdaptiveMediaSourceDiagnosticAvailableEventArgs 提供 RequestId 屬性來關聯不同事件之間的要求資訊。Like the arguments for other adaptive streaming events, the AdaptiveMediaSourceDiagnosticAvailableEventArgs provides a RequestId propery for correlating request information between different events.

private void DiagnosticAvailable(AdaptiveMediaSourceDiagnostics sender, AdaptiveMediaSourceDiagnosticAvailableEventArgs args)
{
    MySendTelemetryFunction(args.RequestId, args.Position,
                            args.DiagnosticType, args.SegmentId,
                            args.ResourceType, args.ResourceUri,
                            args.ResourceDuration, args.ResourceContentType,
                            args.ResourceByteRangeOffset,
                            args.ResourceByteRangeLength, 
                            args.Bitrate,
                            args.ExtendedError);

}

使用 MediaBinder 延遲繫結播放清單中項目的彈性資料流內容Defer binding of adaptive streaming content for items in a playback list by using MediaBinder

MediaBinder 類別可讓您延期繫結 MediaPlaybackList 中的媒體內容。The MediaBinder class allows you to defer binding of media content in a MediaPlaybackList. 從 Windows 10 版本 1703 開始,您可以提供 AdaptiveMediaSource 作為繫結的內容。Starting with Windows 10, version 1703, you can supply an AdaptiveMediaSource as bound content. 延遲繫結彈性媒體來源的程序大部分與繫結其他類型的媒體相同,如媒體項目、播放清單與曲目中所述。The process for deferred binding of an adaptive media source is largely the same as binding other types of media, which is described in Media items, playlists, and tracks.

建立 MediaBinder 執行個體、設定應用程式所定義的 權杖 字串以找出要繫結的內容,以及註冊 Binding 事件。Create a MediaBinder instance, set an app-defined Token string to identify the content to be bound, and register for the Binding event. 呼叫 MediaSource.CreateFromMediaBinder,以從 Binder 建立 MediaSourceCreate a MediaSource from the Binder by calling MediaSource.CreateFromMediaBinder. 然後,從 MediaSource 建立 MediaPlaybackItem,並將它新增到播放清單。Then, create a MediaPlaybackItem from the MediaSource and add it to the playback list.

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

Binding事件處理常式、使用權杖字串找出要繫結的內容,然後呼叫**CreateFromStreamAsync** 或**CreateFromUriAsync** 中的其中一個多載來建立彈性媒體來源。In the Binding event handler, use the token string to identify the content to be bound and then create the adaptive media source by calling one of the overloads of CreateFromStreamAsync or CreateFromUriAsync. 因為這些是非同步方法,所以您應該先呼叫MediaBindingEventArgs.GetDeferral方法,指示系統等候作業完成,再繼續進行。Because these are asynchronous methods, you should first call the MediaBindingEventArgs.GetDeferral method to instruct the system to wait for your operation to complete before continuing. 呼叫**SetAdaptiveMediaSource**,將彈性媒體來源設定為繫結的內容。Set the adaptive media source as the bound content by calling SetAdaptiveMediaSource. 最後,在作業完成之後呼叫Deferral.Complete,指示系統繼續進行。Finally, call Deferral.Complete after your operation is complete to instruct the system to continue.

private async void Binder_Binding_AdaptiveMediaSource(MediaBinder sender, MediaBindingEventArgs args)
{
    var deferral = args.GetDeferral();

    var contentUri = new Uri($"http://contoso.com/media/{args.MediaBinder.Token}");
    AdaptiveMediaSourceCreationResult result = await AdaptiveMediaSource.CreateFromUriAsync(contentUri);

    if (result.MediaSource != null)
    {
        args.SetAdaptiveMediaSource(result.MediaSource);
    }
    args.SetUri(contentUri);

    deferral.Complete();
}

如果您想要將事件處理常式註冊為繫結的彈性媒體來源,則可以在 MediaPlaybackListCurrentItemChanged事件的處理常式中執行這項作業。If you want to register event handlers for the bound adaptive media source, you can do this in the handler for the CurrentItemChanged event of the MediaPlaybackList. CurrentMediaPlaybackItemChangedEventArgs.NewItem屬性會將目前播放的新MediaPlaybackItem包含在清單中。The CurrentMediaPlaybackItemChangedEventArgs.NewItem property contains the new currently playing MediaPlaybackItem in the list. 取得代表新項目的AdaptiveMediaSource執行個體,方法是存取MediaPlaybackItemSource屬性,然後存取媒體來源的AdaptiveMediaSource屬性。Get an instance of the AdaptiveMediaSource representing the new item by accessing the Source property of the MediaPlaybackItem and then the AdaptiveMediaSource property of the media source. 如果新的播放項目不是AdaptiveMediaSource,則這個屬性會是空值,因此您應該先測試是否為空值,再嘗試註冊任何物件事件的處理常式。This property will be null if the new playback item is not an AdaptiveMediaSource, so you should test for null before attempting to register handlers for any of the object's events.

private void AMSMediaPlaybackList_CurrentItemChanged(MediaPlaybackList sender, CurrentMediaPlaybackItemChangedEventArgs args)
{
    if (!(args.NewItem is null))
    {
        var ams = args.NewItem.Source.AdaptiveMediaSource;
        if (!(ams is null))
        {
            ams.PlaybackBitrateChanged += Ams_PlaybackBitrateChanged;
        }
    }
}