Diffusion en continu adaptative

Cet article décrit comment ajouter la lecture de contenus multimédias en streaming adaptatif à une application Universal Windows Platform (UWP). Cette fonctionnalité prend en charge la lecture de contenus de diffusion en continu en direct Http (HLS) et de diffusion dynamique sur HTTP (DASH).

À partir de Windows 10, version 1803, le Streaming fluide est pris en charge par AdaptiveMediaSource. Notez que pour le Streaming fluide, seuls les codecs H264 et WVC1 sont pris en charge. Les autres types de manifestes n’ont pas cette limitation.

Pour une liste des balises du protocole HLS prises en charge, voir Prise en charge des balises HLS.

Pour une liste des profils DASH pris en charge, voir Prise en charge des profils DASH.

Remarque

Le code de cet article a été adapté à partir de l’exemple UWP Adaptive streaming.

Diffusion en continu adaptative simple avec MediaPlayer et MediaPlayerElement

Pour lire des médias en streaming adaptatif dans une application UWP, créez un objet Uri pointant vers un fichier manifeste DASH ou HLS. Créez une instance de la classe MediaPlayer. Appelez MediaSource.CreateFromUri pour créer un nouvel objet MediaSource, puis définissez-le comme propriété Source du MediaPlayer. Appelez Play pour lancer la lecture du contenu multimédia.

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

L’exemple ci-dessus lit l’audio du contenu multimédia, mais ne rend pas automatiquement le contenu dans votre interface utilisateur. La plupart des applications qui lisent du contenu vidéo voudront rendre le contenu dans une page XAML. Pour ce faire, ajoutez un contrôle MediaPlayerElement à votre page XAML.

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

Appelez MediaSource.CreateFromUri pour créer une source multimédia à partir de l’URI d’un fichier manifeste DASH ou HLS. Définissez ensuite la propriété Source du MediaPlayerElement. Le MediaPlayerElement créera automatiquement un nouvel objet MediaPlayer pour le contenu. Vous pouvez appeler Play sur le MediaPlayer pour lancer la lecture du contenu.

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

Remarque

À partir de Windows 10, version 1607, il est recommandé d’utiliser la classe MediaPlayer pour lire les éléments multimédias. Le MediaPlayerElement est un contrôle XAML léger qui est utilisé pour rendre le contenu d’un MediaPlayer dans une page XAML. Le contrôle MediaElement continue d’être pris en charge à des fins de rétrocompatibilité. Pour plus d’informations sur l’utilisation de MediaPlayer et de MediaPlayerElement pour lire des contenus multimédias, voir Lire des fichiers audio et vidéo avec MediaPlayer. Pour plus d’informations sur l’utilisation de MediaSource et des API connexes pour travailler avec du contenu multimédia, voir Éléments multimédias, listes de lecture et pistes.

Diffusion en continu adaptative avec AdaptiveMediaSource

Si votre application nécessite des fonctionnalités de diffusion en continu adaptative plus avancées, comme la fourniture d’en-têtes HTTP personnalisés, la surveillance des débits binaires de téléchargement et de lecture actuels, ou l’ajustement des ratios qui déterminent quand le système change les débits binaires du flux adaptatif, utilisez l’objet AdaptiveMediaSource.

Les API de streaming adaptatif se trouvent dans l’espace de noms Windows.Media.Streaming.Adaptive. Les exemples de cet article utilisent les API des espaces noms suivants.

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

Initialiser un AdaptiveMediaSource à partir d’un URI.

Initialisez l’AdaptiveMediaSource avec l’URI d’un fichier manifeste de streaming adaptatif en appelant CreateFromUriAsync. La valeur AdaptiveMediaSourceCreationStatus renvoyée par cette méthode vous permet de savoir si la source de média a été créée avec succès. Si c’est le cas, vous pouvez définir l’objet comme source de flux pour votre MediaPlayer en créant un objet MediaSource en appelant MediaSource.CreateFromAdaptiveMediaSource, puis en l’affectant à la propriété Source du lecteur multimédia. Dans cet exemple, la propriété AvailableBitrates est interrogée pour déterminer le débit binaire maximal pris en charge pour ce flux, puis cette valeur est définie comme débit binaire initial. Cet exemple enregistre également des gestionnaires pour les différents événements AdaptiveMediaSource qui seront abordés plus loin dans cet 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}");
    }
}

Initialiser une AdaptiveMediaSource avec HttpClient

Si vous avez besoin de définir des en-têtes HTTP personnalisés pour obtenir le fichier manifeste, vous pouvez créer un objet HttpClient, définir les en-têtes désirés, et ensuite passer l’objet dans la surcharge de CreateFromUriAsync.

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

L’événement DownloadRequested est déclenché lorsque le système est sur le point de récupérer une ressource sur le serveur. L’objet AdaptiveMediaSourceDownloadRequestedEventArgs passé dans le gestionnaire de l’événement expose les propriétés qui fournissent des informations sur la ressource demandée, telles que le type et l’URI de la ressource.

Modifier les propriétés de la requête de ressource à l’aide de l’événement DownloadRequested

Vous pouvez utiliser le gestionnaire de l’événement DownloadRequested pour modifier la requête de ressource en mettant à jour les propriétés de l’objet AdaptiveMediaSourceDownloadResult fournies par les args de l’événement. Dans l’exemple ci-dessous, l’URI à partir duquel la ressource sera récupérée est modifié en mettant à jour les propriétés ResourceUri de l’objet Result. Vous pouvez également réécrire le décalage de la plage d’octets et la longueur pour les segments de média ou, comme le montre l’exemple ci-dessous, modifier l’URI de la ressource pour télécharger la ressource complète et définir le décalage de la plage d’octets et la longueur à null.

Vous pouvez remplacer le contenu de la ressource requête en définissant les propriétés Buffer ou InputStream de l’objet Result. Dans l’exemple ci-dessous, le contenu de la ressource manifeste est remplacé en définissant la propriété Buffer. Notez que si vous mettez à jour la requête de ressource avec des données obtenues de manière asynchrone, comme la récupération de données à partir d’un serveur distant ou l’authentification asynchrone de l’utilisateur, vous devez appeler AdaptiveMediaSourceDownloadRequestedEventArgs.GetDeferral pour obtenir un report, puis appeler Complete lorsque l’opération est terminée pour signaler au système que l’opération de requête de téléchargement peut se poursuivre.

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

Utilisez les événements de débit pour gérer les changements de débit et y répondre

L’objet AdaptiveMediaSource fournit des événements qui vous permettent de réagir lorsque les débits binaires de téléchargement ou de lecture changent. Dans cet exemple, les débits actuels sont simplement mis à jour dans l’interface utilisateur. Notez que vous pouvez modifier les ratios qui déterminent quand le système change les débits du flux adaptatif. Pour plus d’informations, consultez la propriété 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();
    }));
}

Gérer les événements de fin de téléchargement et d’échec

L’objet AdaptiveMediaSource déclenche l’événement DownloadFailed lorsque le téléchargement d’une ressource requête échoue. Vous pouvez utiliser cet événement pour mettre à jour votre interface utilisateur en réponse à l’échec. Vous pouvez également utiliser cet événement pour journaliser des informations statistiques sur l’opération de téléchargement et l’échec.

L’objet AdaptiveMediaSourceDownloadFailedEventArgs transmis au gestionnaire de l’événement contient des métadonnées sur l’échec du téléchargement de la ressource, telles que le type de ressource, l’URI de la ressource et la position dans le flux où l’échec s’est produit. La propriété RequestId obtient un identifiant unique généré par le système pour la requête, qui peut être utilisé pour corréler les informations sur l’état d’une requête individuelle dans plusieurs événements.

La propriété Statistics renvoie un objet AdaptiveMediaSourceDownloadStatistics qui fournit des informations détaillées sur le nombre d’octets reçus au moment de l’événement et sur la chronologie des différentes étapes de l’opération de téléchargement. Vous pouvez journaliser ces informations afin d’identifier les problèmes de performance dans votre mise en œuvre de la diffusion en continu adaptative.

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

}

L’événement DownloadCompleted se produit lorsque le téléchargement d’une ressource est terminé et fournit des données similaires à celles de l’événement DownloadFailed. Une fois de plus, un RequestId est fourni pour corréler les événements d’une même requête. Un objet AdaptiveMediaSourceDownloadStatistics est également fourni pour permettre le journal des statistiques de téléchargement.

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

}

Recueillir des données télémétriques sur le streaming adaptatif avec AdaptiveMediaSourceDiagnostics

L’AdaptiveMediaSource expose une propriété Diagnostics qui renvoie un objet AdaptiveMediaSourceDiagnostics. Utilisez cet objet pour vous inscrire à l’événement DiagnosticAvailable. Cet événement est destiné à la collecte de données télémétriques et ne doit pas être utilisé pour modifier le comportement de l’application au moment de l’exécution. Cet événement de diagnostic est déclenché pour de nombreuses raisons différentes. Vérifiez la propriété DiagnosticType de l’objet AdaptiveMediaSourceDiagnosticAvailableEventArgs passé dans l’événement pour déterminer la raison pour laquelle l’événement a été levé. Les raisons potentielles incluent des erreurs d’accès à la ressource requête et des erreurs d’analyse du fichier manifeste de streaming. Pour une liste des situations pouvant déclencher un événement de diagnostic, voir AdaptiveMediaSourceDiagnosticType. Comme les arguments des autres événements de diffusion en continu adaptative, l’AdaptiveMediaSourceDiagnosticAvailableEventArgs fournit une propriété RequestId pour corréler les informations de requête entre les différents événements.

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

}

Différer la liaison du contenu de diffusion en continu adaptative pour les éléments d’une liste de lecture à l’aide de MediaBinder

La classe MediaBinder vous permet de différer la liaison du contenu multimédia dans une liste MediaPlaybackList. À partir de Windows 10, version 1703, vous pouvez fournir une AdaptiveMediaSource comme contenu lié. Le processus de liaison différée d’une source de média adaptatif est en grande partie le même que la liaison d’autres types de médias, qui est décrite dans Éléments multimédias, listes de lecture et pistes.

Créez une instance de MediaBinder, définissez une chaîne Token définie par l’application pour identifier le contenu à lier et inscrivez-vous à l’événement Binding. Créez un MediaSource à partir du Binder en appelant MediaSource.CreateFromMediaBinder. Créez ensuite un élément MediaPlaybackItem à partir du MediaSource et ajoutez-le à la liste de lecture.

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

Dans le gestionnaire de l’événement Binding, utilisez la chaîne de jetons pour identifier le contenu à lier, puis créez la source multimédia adaptative en appelant l’une des surcharges de CreateFromStreamAsync ou CreateFromUriAsync. Comme il s’agit de méthodes asynchrones, vous devez d’abord appeler la méthode MediaBindingEventArgs.GetDeferral pour demander au système d’attendre la fin de votre opération avant de continuer. Définissez la source multimédia adaptative comme contenu lié en appelant SetAdaptiveMediaSource. Enfin, appelez Deferral.Complete une fois l’opération terminée pour demander au système de continuer.

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

Si vous souhaitez enregistrer des gestionnaires d’événements pour la source de média adaptatif liée, vous pouvez le faire dans le gestionnaire de l’événement CurrentItemChanged de la liste MediaPlaybackList. La propriété CurrentMediaPlaybackItemChangedEventArgs.NewItem contient le nouvel élément MediaPlayback en cours de lecture dans la liste. Obtenez une instance de AdaptiveMediaSource représentant le nouvel élément en accédant à la propriété Source du MediaPlaybackItem puis à la propriété AdaptiveMediaSource de la source de média. Cette propriété sera nulle si le nouvel élément de lecture n’est pas une AdaptiveMediaSource, vous devez donc tester la présence de null avant d’essayer d’enregistrer des gestionnaires pour les événements de l’objet.

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