Streaming adaptif

Artikel ini menjelaskan cara menambahkan pemutaran konten multimedia streaming adaptif ke aplikasi Universal Windows Platform (UWP). Fitur ini mendukung pemutaran konten Http Live Streaming (HLS) dan Dynamic Streaming melalui HTTP (DASH).

Dimulai dengan Windows 10, versi 1803, Smooth Streaming didukung oleh AdaptiveMediaSource. Perhatikan bahwa untuk Smoooth Streaming, hanya codec H264 dan WVC1 yang didukung. Jenis manifes lainnya tidak memiliki batasan ini.

Untuk daftar tag protokol HLS yang didukung, lihat dukungan tag HLS.

Untuk daftar profil DASH yang didukung, lihat dukungan profil DASH.

Catatan

Kode dalam artikel ini diadaptasi dari sampel streaming UWP Adaptive.

Streaming adaptif sederhana dengan MediaPlayer dan MediaPlayerElement

Untuk memutar media streaming adaptif di aplikasi UWP, buat objek Uri yang menunjuk ke file manifes DASH atau HLS. Buat contoh kelas MediaPlayer . Panggil MediaSource.CreateFromUri untuk membuat objek MediaSource baru lalu atur ke properti SumberMediaPlayer. Putar Panggilan untuk memulai pemutaran konten media.

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

Contoh di atas akan memutar audio konten media tetapi tidak secara otomatis merender konten di UI Anda. Sebagian besar aplikasi yang memutar konten video ingin merender konten di halaman XAML. Untuk melakukan ini, tambahkan kontrol MediaPlayerElement ke halaman XAML Anda.

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

Hubungi MediaSource.CreateFromUri untuk membuat MediaSource dari URI file manifes DASH atau HLS. Kemudian atur properti SumbermediaPlayerElement. MediaPlayerElement akan secara otomatis membuat objek MediaPlayer baru untuk konten. Anda dapat memanggil Play di MediaPlayer untuk memulai pemutaran konten.

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

Catatan

Dimulai dengan Windows 10, versi 1607, disarankan agar Anda menggunakan kelas MediaPlayer untuk memutar item media. MediaPlayerElement adalah kontrol XAML ringan yang digunakan untuk merender konten MediaPlayer di halaman XAML. Kontrol MediaElement terus didukung untuk kompatibilitas mundur. Untuk informasi selengkapnya tentang menggunakan MediaPlayer dan MediaPlayerElement untuk memutar konten media, lihat Memutar audio dan video dengan MediaPlayer. Untuk informasi tentang menggunakan MediaSource dan API terkait untuk bekerja dengan konten media, lihat Item media, daftar putar, dan trek.

Streaming adaptif dengan AdaptiveMediaSource

Jika aplikasi Anda memerlukan fitur streaming adaptif yang lebih canggih, seperti menyediakan header HTTP khusus, memantau bitrate unduhan dan pemutaran saat ini, atau menyesuaikan rasio yang menentukan kapan sistem mengalihkan bitrate aliran adaptif, gunakan objek AdaptiveMediaSource .

API streaming adaptif ditemukan di Windows. Media.Streaming.Adaptif namespace. Contoh dalam artikel ini menggunakan API dari ruang nama berikut.

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

Menginisialisasi AdaptiveMediaSource dari URI.

Menginisialisasi AdaptiveMediaSource dengan URI file manifes streaming adaptif dengan memanggil CreateFromUriAsync. Nilai AdaptiveMediaSourceCreationStatus yang dikembalikan dari metode ini memberi tahu Anda jika sumber media berhasil dibuat. Jika demikian, Anda dapat mengatur objek sebagai sumber aliran untuk MediaPlayer Anda dengan membuat objek MediaSource dengan memanggil MediaSource.CreateFromAdaptiveMediaSource, dan kemudian menetapkannya ke properti Sumber pemutar media. Dalam contoh ini, properti AvailableBitrates dikuk kalikan untuk menentukan bitrate maksimum yang didukung untuk aliran ini, lalu nilai tersebut ditetapkan sebagai bitrate awal. Contoh ini juga mendaftarkan penangan untuk beberapa peristiwa AdaptiveMediaSource yang dibahas nanti di artikel ini.

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

Menginisialisasi AdaptiveMediaSource menggunakan HttpClient

Jika Anda perlu mengatur header HTTP kustom untuk mendapatkan file manifes, Anda dapat membuat objek HttpClient , mengatur header yang diinginkan, dan kemudian meneruskan objek ke kelebihan beban CreateFromUriAsync.

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

Acara DownloadRequested dinaikkan ketika sistem akan mengambil sumber daya dari server. AdaptiveMediaSourceDownloadRequestedEventArgs yang diteruskan ke event handler mengekspos properti yang memberikan informasi tentang sumber daya yang diminta seperti jenis dan URI sumber daya.

Memodifikasi properti permintaan sumber daya menggunakan acara DownloadRequested

Anda dapat menggunakan penangan peristiwa DownloadRequested untuk memodifikasi permintaan sumber daya dengan memperbarui properti objek AdaptiveMediaSourceDownloadResult yang disediakan oleh args acara. Pada contoh di bawah ini, URI dari mana sumber daya akan diambil dimodifikasi dengan memperbarui properti ResourceUri dari objek hasil. Anda juga dapat menulis ulang offset dan panjang rentang byte untuk segmen media atau, seperti yang ditunjukkan contoh di bawah ini, mengubah sumber daya URI untuk mengunduh sumber daya penuh dan mengatur offset dan panjang rentang byte menjadi nol.

Anda dapat mengganti konten sumber daya yang diminta dengan mengatur properti Buffer atau InputStream dari objek hasil. Pada contoh di bawah ini, konten sumber daya manifes diganti dengan mengatur properti Buffer . Perhatikan bahwa jika Anda memperbarui permintaan sumber daya dengan data yang diperoleh secara asinkron, seperti mengambil data dari server jarak jauh atau otentikasi pengguna asinkron, Anda harus memanggil AdaptiveMediaSourceDownloadRequestedEventArgs.GetDeferral untuk mendapatkan penangguhan dan kemudian menelepon Selesai ketika operasi selesai untuk memberi sinyal sistem bahwa operasi permintaan unduhan dapat dilanjutkan.

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

Gunakan peristiwa bitrate untuk mengelola dan merespons perubahan bitrate

Objek AdaptiveMediaSource menyediakan peristiwa yang memungkinkan Anda bereaksi saat bitrate unduhan atau pemutaran berubah. Dalam contoh ini, bitrate saat ini hanya diperbarui di UI. Perhatikan bahwa Anda dapat mengubah rasio yang menentukan kapan sistem beralih bitrate aliran adaptif. Untuk informasi selengkapnya, lihat properti AdvancedSettings .

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

Menangani penyelesaian unduhan dan peristiwa kegagalan

Objek AdaptiveMediaSource memunculkan peristiwa DownloadFailed ketika pengunduhan sumber daya yang diminta gagal. Anda dapat menggunakan acara ini untuk memperbarui UI Anda sebagai respons atas kegagalan tersebut. Anda juga dapat menggunakan acara tersebut untuk mencatat informasi statistik tentang operasi pengunduhan dan kegagalan.

Objek AdaptiveMediaSourceDownloadFailedEventArgs yang diteruskan ke penangan peristiwa berisi metadata tentang unduhan sumber daya yang gagal, seperti jenis sumber daya, URI sumber daya, dan posisi dalam aliran tempat kegagalan terjadi. RequestId mendapatkan pengidentifikasi unik yang dibuat sistem untuk permintaan yang dapat digunakan untuk menghubungkan informasi status tentang permintaan individual di beberapa peristiwa.

Properti Statistik mengembalikan objek AdaptiveMediaSourceDownloadStatistics yang memberikan informasi terperinci tentang jumlah byte yang diterima pada saat acara dan waktu berbagai tonggak dalam operasi pengunduhan. Anda dapat mencatat informasi ini untuk mengidentifikasi masalah perfomance dalam implementasi streaming adaptif Anda.

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

}

Peristiwa DownloadCompleted terjadi ketika unduhan sumber daya selesai dan menghasilkan data yang mirip dengan acara DownloadFailed . Sekali lagi, RequestId disediakan untuk menghubungkan acara untuk satu permintaan. Selain itu, objek AdaptiveMediaSourceDownloadStatistics disediakan untuk mengaktifkan pencatatan statistik unduhan.

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

}

Kumpulkan data telemetri streaming adaptif dengan AdaptiveMediaSourceDiagnostics

AdaptiveMediaSource mengekspos properti Diagnostik yang mengembalikan objek AdaptiveMediaSourceDiagnostics. Gunakan objek ini untuk mendaftar ke acara DiagnosticAvailable . Acara ini dimaksudkan untuk digunakan untuk pengumpulan telemetri dan tidak boleh digunakan untuk memodifikasi perilaku aplikasi saat runtime. Acara diagnostik ini diangkat karena berbagai alasan. Periksa properti DiagnosticType dari objek AdaptiveMediaSourceDiagnosticAvailableEventArgs yang diteruskan ke acara untuk menentukan alasan acara dinaikkan. Alasan potensial termasuk kesalahan mengakses sumber daya yang diminta dan kesalahan menguraikan file manifes streaming. Untuk daftar situasi yang dapat memicu peristiwa diagnostik, lihat AdaptiveMediaSourceDiagnosticType. Seperti argumen untuk acara streaming adaptif lainnya, AdaptiveMediaSourceDiagnosticAvailableEventArgs menyediakan propery RequestId untuk menghubungkan informasi permintaan antara peristiwa yang berbeda.

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

}

Menunda pengikatan konten streaming adaptif untuk item dalam daftar pemutaran dengan menggunakan MediaBinder

Kelas MediaBinder memungkinkan Anda untuk menunda pengikatan konten media di MediaPlaybackList. Dimulai dengan Windows 10, versi 1703, Anda dapat menyediakan AdaptiveMediaSource sebagai konten terikat. Proses untuk menunda pengikatan sumber media adaptif sebagian besar sama dengan mengikat jenis media lain, yang dijelaskan dalam item Media, daftar putar, dan trek.

Buat instans MediaBinder , atur string Token yang ditentukan aplikasi untuk mengidentifikasi konten yang akan diikat, dan daftar untuk acara Binding . Buat MediaSource dari Binder dengan memanggil MediaSource.CreateFromMediaBinder. Kemudian, buat MediaPlaybackItem dari MediaSource dan tambahkan ke daftar pemutaran.

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

Di penangan peristiwa Pengikatan , gunakan string token untuk mengidentifikasi konten yang akan diikat dan kemudian buat sumber media adaptif dengan memanggil salah satu kelebihan beban CreateFromStreamAsync atau CreateFromUriAsync. Karena ini adalah metode asinkron, Anda harus terlebih dahulu memanggil metode MediaBindingEventArgs.GetDeferral untuk menginstruksikan sistem menunggu operasi Anda selesai sebelum melanjutkan. Atur sumber media adaptif sebagai konten terikat dengan memanggil SetAdaptiveMediaSource. Terakhir, hubungi Deferral.Complete setelah operasi Anda selesai untuk menginstruksikan sistem untuk melanjutkan.

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

Jika Anda ingin mendaftarkan penangan peristiwa untuk sumber media adaptif terikat, Anda dapat melakukan ini di penangan untuk acara CurrentItemChanged dari MediaPlaybackList. Properti CurrentMediaPlaybackItemChangedEventArgs.NewItem berisi MediaPlaybackItem baru yang sedang diputar dalam daftar. Dapatkan contoh AdaptiveMediaSource yang mewakili item baru dengan mengakses properti SumberMediaPlaybackItem dan kemudian properti AdaptiveMediaSource dari sumber media. Properti ini akan null jika item pemutaran baru bukan AdaptiveMediaSource, jadi Anda harus menguji null sebelum mencoba mendaftarkan penangan untuk salah satu peristiwa objek.

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