Udostępnij za pośrednictwem


Adaptacyjne przesyłanie strumieniowe

W tym artykule opisano sposób dodawania odtwarzania adaptacyjnych treści multimedialnych strumieniowych do aplikacji WinUI. Ta funkcja obsługuje odtwarzanie zawartości Http Live Streaming (HLS) i dynamicznego przesyłania strumieniowego za pośrednictwem protokołu HTTP (DASH).

Począwszy od systemu Windows 10 w wersji 1803, Smooth Streaming jest obsługiwany przez AdaptiveMediaSource. Należy pamiętać, że w przypadku funkcji Smooth Streaming obsługiwane są tylko koderki H264 i WVC1. Inne typy manifestów nie mają tego ograniczenia.

Aby uzyskać listę obsługiwanych tagów protokołu HLS, zobacz Obsługa tagów HLS.

Aby uzyskać listę obsługiwanych profilów DASH, zobacz Obsługa profilów DASH.

Uwaga / Notatka

Kod w tym artykule został dostosowany z przykładu adaptacyjnego przesyłania strumieniowego.

Proste adaptacyjne przesyłanie strumieniowe za pomocą odtwarzacza MediaPlayer i MediaPlayerElement

Aby odtwarzać adaptacyjne multimedia strumieniowe w aplikacji WinUI, utwórz obiekt Uri wskazujący na plik manifestu DASH lub HLS. Utwórz wystąpienie klasy MediaPlayer . Wywołaj metodę MediaSource.CreateFromUri , aby utworzyć nowy obiekt MediaSource , a następnie ustaw go na właściwość Source elementu MediaPlayer. Wywołaj Odtwórz, aby rozpocząć odtwarzanie zawartości multimedialnej.

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

Powyższy przykład odtwarza dźwięk zawartości multimedialnej, ale nie renderuje automatycznie zawartości w interfejsie użytkownika. Większość aplikacji, które odtwarzają zawartość wideo, będzie chciała renderować zawartość na stronie XAML. W tym celu dodaj kontrolkę MediaPlayerElement do strony XAML.

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

Wywołaj metodę MediaSource.CreateFromUri, aby utworzyć MediaSource z identyfikatora URI pliku manifestu DASH lub HLS. Następnie ustaw właściwość Sourceelementu MediaPlayerElement. Element MediaPlayerElement automatycznie utworzy nowy obiekt MediaPlayer dla zawartości. Możesz wywołać funkcję Play na odtwarzaczu MediaPlayer , aby rozpocząć odtwarzanie zawartości.

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

Adaptacyjne przesyłanie strumieniowe za pomocą funkcji AdaptiveMediaSource

Jeśli aplikacja wymaga bardziej zaawansowanych funkcji adaptacyjnego przesyłania strumieniowego, takich jak udostępnianie niestandardowych nagłówków HTTP, monitorowanie bieżących szybkości pobierania i odtwarzania bitów lub dostosowywanie współczynników, które określają, kiedy system przełącza szybkości transmisji bitów strumienia adaptacyjnego, użyj obiektu AdaptiveMediaSource .

Zainicjuj element AdaptiveMediaSource z identyfikatora URI.

Zainicjuj interfejs AdaptiveMediaSource za pomocą identyfikatora URI manifestu przesyłania strumieniowego adaptacyjnego, wywołując CreateFromUriAsync. Wartość AdaptiveMediaSourceCreationStatus zwrócona z tej metody informuje, czy źródło multimediów zostało utworzone pomyślnie. Jeśli tak, możesz ustawić obiekt jako źródło strumienia dla elementu MediaPlayer, tworząc obiekt MediaSource, wywołując element MediaSource.CreateFromAdaptiveMediaSource, a następnie przypisując go do właściwości Source odtwarzacza multimediów. W tym przykładzie właściwość AvailableBitrates jest odpytywana w celu określenia maksymalnej obsługiwanej szybkości transmisji bitów dla tego strumienia, a następnie ta wartość jest ustawiana jako początkowa szybkość transmisji bitów. W tym przykładzie zarejestrowano również programy obsługi dla kilku zdarzeń AdaptiveMediaSource , które zostały omówione w dalszej części tego artykułu.

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

Inicjowanie elementu AdaptiveMediaSource przy użyciu obiektu HttpClient

Jeśli chcesz ustawić niestandardowe nagłówki HTTP na potrzeby pobierania pliku manifestu, możesz utworzyć obiekt HttpClient , ustawić żądane nagłówki, a następnie przekazać obiekt do przeciążenia metody CreateFromUriAsync.

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

Zdarzenie DownloadRequested jest zgłaszane, gdy system ma pobrać zasób z serwera. Element AdaptiveMediaSourceDownloadRequestedEventArgs przekazany do programu obsługi zdarzeń uwidacznia właściwości, które zawierają informacje o żądanym zasobie, takie jak typ i identyfikator URI zasobu.

Modyfikowanie właściwości żądania zasobu przy użyciu zdarzenia DownloadRequested

Aby zmodyfikować żądanie zasobu, można użyć procedury obsługi zdarzeń DownloadRequested , aktualizując właściwości obiektu AdaptiveMediaSourceDownloadResult dostarczonego przez args zdarzenia. W poniższym przykładzie identyfikator URI, z którego zostanie pobrany zasób, zostanie zmodyfikowany przez zaktualizowanie właściwości ResourceUri obiektu wynikowego. Możesz również przepisać przesunięcie i długość zakresu bajtów dla segmentów multimediów lub, jak pokazano w poniższym przykładzie, zmień identyfikator URI zasobu, aby pobrać pełny zasób i ustawić przesunięcie zakresu bajtów i długość na wartość null.

Zawartość żądanego zasobu można zastąpić, ustawiając właściwości Buffer lub InputStream obiektu wynikowego. W poniższym przykładzie zawartość zasobu manifestu jest zastępowana przez ustawienie właściwości Bufor . Należy pamiętać, że w przypadku aktualizowania żądania zasobu przy użyciu danych uzyskanych asynchronicznie, takich jak pobieranie danych z serwera zdalnego lub asynchronicznego uwierzytelniania użytkownika, należy wywołać metodę AdaptiveMediaSourceDownloadRequestedEventArgs.GetDeferral , aby uzyskać odroczenie, a następnie wywołać metodę Complete po zakończeniu operacji sygnalizowania systemu, że operacja żądania pobierania może być kontynuowana.

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

Używaj zdarzeń szybkości transmisji bitów, aby zarządzać zmianami i na nie reagować.

Obiekt AdaptiveMediaSource udostępnia zdarzenia, które umożliwiają reagowanie na zmianę szybkości pobierania lub odtwarzania bitów. W tym przykładzie bieżące szybkości bitów są po prostu aktualizowane w interfejsie użytkownika. Należy pamiętać, że można zmodyfikować współczynniki, które określają, kiedy system przełącza szybkości transmisji bitów strumienia adaptacyjnego. Aby uzyskać więcej informacji, zobacz właściwość AdvancedSettings .

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

Obsługa zdarzeń ukończenia pobierania i niepowodzeń

Obiekt AdaptiveMediaSource zgłasza zdarzenie DownloadFailed , gdy pobieranie żądanego zasobu zakończy się niepowodzeniem. To zdarzenie służy do aktualizowania interfejsu użytkownika w odpowiedzi na błąd. Możesz również użyć zdarzenia, aby rejestrować informacje statystyczne dotyczące operacji pobierania i niepowodzenia.

Obiekt AdaptiveMediaSourceDownloadFailedEventArgs przekazany do procedury obsługi zdarzeń zawiera metadane dotyczące nieudanego pobierania zasobu, takie jak typ zasobu, identyfikator URI zasobu i położenie w strumieniu, w którym wystąpił błąd. RequestId to unikatowy identyfikator generowany przez system dla żądania, którego można użyć do korelowania informacji o stanie pojedynczego żądania w wielu zdarzeniach.

Właściwość Statistics zwraca obiekt AdaptiveMediaSourceDownloadStatistics , który zawiera szczegółowe informacje o liczbie bajtów odebranych w czasie zdarzenia i chronometrażu różnych kamieni milowych w operacji pobierania. Te informacje można rejestrować w celu zidentyfikowania problemów z wydajnością w implementacji adaptacyjnego przesyłania strumieniowego.

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

}

Zdarzenie DownloadCompleted występuje po zakończeniu pobierania zasobu i udostępni podobne dane do zdarzenia DownloadFailed . Po raz kolejny parametr RequestId jest dostarczany do korelowania zdarzeń dla pojedynczego żądania. Ponadto obiekt AdaptiveMediaSourceDownloadStatistics umożliwia rejestrowanie statystyk pobierania.

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

}

Zbieranie danych telemetrycznych dla adaptacyjnego przesyłania strumieniowego za pomocą AdaptiveMediaSourceDiagnostics

Element AdaptiveMediaSource uwidacznia właściwość Diagnostics , która zwraca obiekt AdaptiveMediaSourceDiagnostics . Użyj tego obiektu, aby zarejestrować się w przypadku zdarzenia DiagnosticAvailable . To zdarzenie ma być używane do zbierania danych telemetrycznych i nie powinno być używane do modyfikowania zachowania aplikacji w czasie wykonywania. To zdarzenie diagnostyczne jest zgłaszane z wielu różnych powodów. Sprawdź właściwość DiagnosticType obiektu AdaptiveMediaSourceDiagnosticAvailableEventArgs przekazanego do zdarzenia, aby określić przyczynę zgłoszenia zdarzenia. Potencjalne przyczyny obejmują błędy podczas uzyskiwania dostępu do żądanego zasobu i błędy analizowania pliku manifestu przesyłania strumieniowego. Aby uzyskać listę sytuacji, które mogą wyzwalać zdarzenie diagnostyczne, zobacz AdaptiveMediaSourceDiagnosticType. Podobnie jak argumenty innych zdarzeń adaptacyjnego przesyłania strumieniowego, element AdaptiveMediaSourceDiagnosticAvailableEventArgs udostępnia właściwość RequestId do korelowania informacji o żądaniu między różnymi zdarzeniami.

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

}

Odrocz wiązanie adaptacyjnej zawartości przesyłania strumieniowego dla elementów listy odtwarzania za pomocą MediaBinder

Klasa MediaBinder umożliwia odroczenie powiązania zawartości multimedialnej w obiekcie MediaPlaybackList. Począwszy od systemu Windows 10 w wersji 1703, można podać element AdaptiveMediaSource jako powiązaną zawartość. Proces odroczonego powiązania adaptacyjnego źródła multimediów jest w dużej mierze taki sam jak powiązanie innych typów multimediów, które opisano w temacie Elementy multimedialne, listy odtwarzania i ścieżki.

Utwórz wystąpienie MediaBinder, ustaw ciąg Token zdefiniowany przez aplikację, aby zidentyfikować zawartość do powiązania, i zarejestruj się na zdarzenie Binding. Utwórz element MediaSource na podstawie narzędzia Binder , wywołując element MediaSource.CreateFromMediaBinder. Następnie utwórz obiekt MediaPlaybackItem z witryny MediaSource i dodaj go do listy odtwarzania.

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

W obsłudze zdarzenia Binding użyj ciągu znaków tokenu, aby zidentyfikować zawartość do powiązania, a następnie utwórz elastyczne źródło multimedialne przez wywołanie jednego z przeciążeń CreateFromStreamAsync lub CreateFromUriAsync. Ponieważ są to metody asynchroniczne, należy najpierw wywołać metodę MediaBindingEventArgs.GetDeferral , aby system czekał na zakończenie operacji przed kontynuowaniem. Ustaw źródło multimediów adaptacyjnych jako powiązaną zawartość, wywołując element SetAdaptiveMediaSource. Na koniec wywołaj metodę Deferral.Complete po zakończeniu operacji, aby poinstruować system, aby kontynuować.

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

Jeśli chcesz zarejestrować programy obsługi zdarzeń dla powiązanego adaptacyjnego źródła mediów, możesz to zrobić w procedurze obsługi zdarzenia CurrentItemChanged dla MediaPlaybackList. Właściwość CurrentMediaPlaybackItemChangedEventArgs.NewItem zawiera nowy, aktualnie odtwarzany element MediaPlaybackItem na liście. Pobierz wystąpienie elementu AdaptiveMediaSource reprezentujące nowy element, korzystając z właściwości Source obiektu MediaPlaybackItem , a następnie właściwości AdaptiveMediaSource źródła multimediów. Ta właściwość będzie mieć wartość null, jeśli nowy element odtwarzania nie jest elementem AdaptiveMediaSource, dlatego przed podjęciem próby zarejestrowania programów obsługi dla dowolnego z zdarzeń obiektu należy przetestować wartość null.

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