Adaptivní streamování

Tento článek popisuje, jak do aplikace WinUI přidat přehrávání multimediálního obsahu s adaptivním streamováním. Tato funkce podporuje přehrávání obsahu HTTP Live Streaming (HLS) a dynamického streamování přes HTTP (DASH).

Od Windows 10 verze 1803 podporuje Smooth Streaming AdaptiveMediaSource. Upozorňujeme, že u technologie Smooth Streaming se podporují pouze kodeky H264 a WVC1. Jiné typy manifestu toto omezení nemají.

Seznam podporovaných značek protokolu HLS najdete v tématu Podpora značek HLS.

Seznam podporovaných profilů DASH najdete v tématu Podpora profilu DASH.

Poznámka:

Kód v tomto článku byl upraven z ukázky adaptivního streamování.

Jednoduché adaptivní streamování s MediaPlayerem a MediaPlayerElement

Pokud chcete přehrávat adaptivní streamovaná média v aplikaci WinUI, vytvořte objekt URI odkazující na soubor manifestu DASH nebo HLS. Vytvořte instanci třídy MediaPlayer . Pro vytvoření nového objektu MediaSource zavolejte MediaSource.CreateFromUri a poté jej nastavte na vlastnost Source u MediaPlayer. Voláním funkce Přehrát zahájíte přehrávání mediálního obsahu.

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

Výše uvedený příklad přehraje zvuk multimediálního obsahu, ale automaticky nevykreslí obsah v uživatelském rozhraní. Většina aplikací, které přehrávají videoobsáh, bude chtít vykreslit obsah na stránce XAML. Uděláte to tak, že na stránku XAML přidáte ovládací prvek MediaPlayerElement .

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

Použijte MediaSource.CreateFromUri pro vytvoření MediaSource z identifikátoru URI souboru manifestu DASH nebo HLS. Potom nastavte Vlastnost SourceMediaPlayerElement. MediaPlayerElement automaticky vytvoří nový Objekt MediaPlayer pro obsah. Pokud chcete začít přehrávat obsah, můžete volat Přehrát na MediaPlayeru .

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

Adaptivní streamování s AdaptiveMediaSource

Pokud vaše aplikace vyžaduje pokročilejší funkce adaptivního streamování, jako je poskytování vlastních hlaviček HTTP, monitorování aktuálních přenosových rychlostí stahování a přehrávání nebo úprava poměrů, které určují, kdy systém přepne přenosovou rychlost adaptivního streamu, použijte objekt AdaptiveMediaSource .

Inicializujte AdaptiveMediaSource z identifikátoru URI.

Inicializace AdaptivníMediaSource s identifikátorem URI souboru manifestu adaptivního streamování voláním CreateFromUriAsync. Hodnota AdaptiveMediaSourceCreationStatus vrácená z této metody vám umožní zjistit, jestli byl zdroj médií úspěšně vytvořen. Pokud ano, můžete objekt nastavit jako zdroj streamu pro MediaPlayer vytvořením MediaSource objektu voláním MediaSource.CreateFromAdaptiveMediaSource a jeho přiřazením k vlastnosti Zdroj přehrávače médií. V tomto příkladu se vlastnost AvailableBitrates dotazuje, aby určila maximální podporovanou přenosovou rychlost pro tento datový proud a pak se tato hodnota nastaví jako počáteční přenosová rychlost. Tento příklad také registruje obslužné rutiny pro několik událostí AdaptiveMediaSource , které jsou popsány dále v tomto článku.

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

Inicializujte AdaptiveMediaSource pomocí HttpClient.

Pokud potřebujete nastavit vlastní hlavičky HTTP pro získání souboru manifestu, můžete vytvořit objekt HttpClient , nastavit požadované hlavičky a pak předat objekt do přetížení CreateFromUriAsync.

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

Událost DownloadRequested je vyvolána, když se systém chystá načíst prostředek ze serveru. AdaptivníMediaSourceDownloadRequestedEventArgs předaný do obslužné rutiny události zveřejňuje vlastnosti, které poskytují informace o požadovaném prostředku, jako je typ a identifikátor URI prostředku.

Úprava vlastností žádosti o prostředek pomocí události DownloadRequested

Obslužnou rutinu události DownloadRequested můžete použít k úpravě požadavku na prostředek tím, že aktualizujete vlastnosti objektu AdaptiveMediaSourceDownloadResult poskytovaného argumenty události. V následujícím příkladu je identifikátor URI, ze kterého se prostředek načte, upraven aktualizací vlastností ResourceUri objektu výsledku. Můžete také přepsat posun a délku rozsahu bajtů pro segmenty médií nebo podle následujícího příkladu změnit identifikátor URI prostředku tak, aby stáhl celý prostředek a nastavil posun rozsahu bajtů a délku na hodnotu null.

Obsah požadovaného prostředku můžete přepsat nastavením vlastnosti Buffer nebo InputStream výsledného objektu. V následujícím příkladu se obsah prostředku manifestu nahradí nastavením vlastnosti Buffer. Všimněte si, že pokud provádíte aktualizaci požadavku na prostředek daty získanými asynchronně, například načtením dat ze vzdáleného serveru nebo asynchronního ověřování uživatele, je třeba volat AdaptiveMediaSourceDownloadRequestedEventArgs.GetDeferral k získání odkladu a po dokončení operace zavolejte Complete, abyste signalizovali systému, že operace žádosti o stažení může pokračovat.

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

Použití událostí přenosových rychlostí ke správě změn přenosové rychlosti a reagování na ně

AdaptivníMediaSource objekt poskytuje události, které umožňují reagovat při změně stahování nebo přehrávání přenosových rychlostí. V tomto příkladu se aktuální přenosová rychlost jednoduše aktualizují v uživatelském rozhraní. Všimněte si, že můžete upravit poměry, které určují, kdy systém přepne přenosovou rychlost adaptivního datového proudu. Další informace naleznete v AdvancedSettings vlastnost.

private void DownloadBitrateChanged(AdaptiveMediaSource sender, AdaptiveMediaSourceDownloadBitrateChangedEventArgs args)
{
    DispatcherQueue.TryEnqueue(() =>
    {
        tbDownloadBitrate.Text = args.NewValue.ToString();
    });
}

private void PlaybackBitrateChanged(AdaptiveMediaSource sender, AdaptiveMediaSourcePlaybackBitrateChangedEventArgs args)
{
    DispatcherQueue.TryEnqueue(() =>
    {
        tbPlaybackBitrate.Text = args.NewValue.ToString();
    });
}

Zpracování událostí dokončení stahování a selhání

AdaptivníMediaSource objekt vyvolá událost DownloadFailed, když stahování požadovaného prostředku selže. Tuto událost můžete použít k aktualizaci uživatelského rozhraní v reakci na selhání. Událost můžete použít také k protokolování statistických informací o operaci stahování a selhání.

Objekt AdaptiveMediaSourceDownloadFailedEventArgs předaný do obslužné rutiny události obsahuje metadata o neúspěšném stažení prostředku, jako je typ prostředku, identifikátor URI prostředku a pozice v datovém proudu, kde došlo k selhání. RequestId získá systémově vygenerovaný jedinečný identifikátor požadavku, který lze použít ke korelaci informací o stavu jednotlivých požadavků napříč několika událostmi.

Vlastnost Statistika vrací Adaptivní-Media-Source-Download-Statistics objekt, který poskytuje detailní informace o počtu bajtů přijatých k momentu události a časování různých milníků v operaci stahování. Tyto informace můžete protokolovat, abyste zjistili problémy s výkonem při implementaci adaptivního streamování.

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

}

Událost DownloadCompleted nastane po dokončení stahování prostředku a poskytuje podobná data jako událost DownloadFailed . Opět je k dispozici ID požadavku pro korelaci událostí pro jeden požadavek. K dispozici je také objekt AdaptiveMediaSourceDownloadStatistics, který umožňuje protokolování statistik stahování.

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

}

Shromažďujte telemetrická data adaptivního streamování pomocí AdaptivníMediaSourceDiagnostika

AdaptivníMediaSource zveřejňuje diagnostickou vlastnost, která vrací AdaptivníMediaSourceDiagnostics objekt. Tento objekt použijte k registraci události DiagnosticAvailable . Tato událost je určená k použití pro shromažďování telemetrických dat a neměla by se používat ke změně chování aplikace za běhu. Tato diagnostická událost je vyvolána z mnoha různých důvodů. Zkontrolujte vlastnost DiagnosticType objektu AdaptiveMediaSourceDiagnosticAvailableEventArgs, který je k dispozici při události, abyste zjistili důvod, proč byla událost vyvolána. Mezi možné důvody patří chyby při přístupu k požadovanému prostředku a chyby při analýze souboru manifestu streamování. Seznam situací, které mohou vyvolat diagnostickou událost, viz AdaptivníMediaSourceDiagnosticType. Podobně jako argumenty pro jiné události adaptivního streamování obsahuje AdaptiveMediaSourceDiagnosticAvailableEventArgs vlastnost RequestId pro korelaci informací o požadavku mezi různými událostmi.

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

}

Odložit vazbu adaptivního streamovaného obsahu pro položky v seznamu přehrávání pomocí MediaBinderu

MediaBinder třída umožňuje odložit vazbu multimediálního obsahu v MediaPlaybackList. Počínaje Windows 10 verzí 1703 můžete použít jako svázaný obsah AdaptiveMediaSource. Proces odložené vazby adaptivního zdroje médií je z velké části stejný jako vazba jiných typů médií, které jsou popsány v položkách médií, seznamech skladeb a stopách.

Vytvořte instanci MediaBinder, nastavte aplikací definovaný řetězec Token k identifikaci obsahu, který se má svázat, a zaregistrujte se k události Binding. Vytvořte MediaSource z Binderu voláním MediaSource.CreateFromMediaBinder. Potom vytvořte MediaPlaybackItem z MediaSource a přidejte ho do seznamu přehrávání.

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

V obslužné rutině události Binding použijte řetězec tokenu k identifikaci obsahu, který má být připojen, a pak vytvořte adaptivní zdroj multimédií voláním jednoho z přetížení CreateFromStreamAsync nebo CreateFromUriAsync. Vzhledem k tomu, že se jedná o asynchronní metody, měli byste nejprve volat metodu MediaBindingEventArgs.GetDeferral , aby systém před pokračováním čekal na dokončení operace. Nastavte adaptivní zdroj médií jako vázaného obsahu voláním SetAdaptiveMediaSource. Nakonec zavolejte Deferral.Complete po dokončení operace, aby systém pokračoval.

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

Pokud chcete zaregistrovat obslužné rutiny událostí pro vázaný zdroj adaptivního média, můžete to udělat v obslužné rutině pro CurrentItemChanged události MediaPlaybackList. CurrentMediaPlaybackItemChangedEventArgs.NewItem vlastnost obsahuje nově aktuálně přehrávaný MediaPlaybackItem v seznamu. Získejte instanci AdaptivníMediaSource představující novou položku přístupem ke zdrojové vlastnosti MediaPlaybackItem a potom AdaptivníMediaSource vlastnost zdroje médií. Tato vlastnost bude null, pokud nová položka přehrávání není AdaptivníMediaSource, takže byste měli otestovat hodnotu null před pokusem o registraci obslužných rutin pro všechny události objektu.

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