Diffusion en continu adaptative

Cet article décrit comment ajouter la lecture de contenu multimédia en streaming adaptatif à une application de plateforme Windows universelle (UWP). Cette fonctionnalité prend en charge la lecture du contenu HLS (Http Live Streaming) et Dynamic Streaming sur HTTP (DASH).

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

Pour obtenir la liste des balises de protocole HLS prises en charge, voir Prise en charge des balises HLS.

Pour obtenir la liste des profils DASH pris en charge, consultez Prise en charge des profils DASH.

Notes

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

Streaming adaptatif avec MediaPlayer et MediaPlayerElement

Pour lire du contenu multimédia en streaming adaptatif dans une application UWP, créez un objet URI pointant sur un fichier manifeste HLS ou DASH. Créez un instance de la classe MediaPlayer. Appelez MediaSource.CreateFromUri pour créer un objet MediaSource, puis définissez-le sur la propriété Source de MediaPlayer. Appelez Play pour démarrer 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 n’affiche pas automatiquement le contenu dans votre interface Utilisateur. La plupart des applications qui lisent du contenu vidéo veulent restituer 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 un MediaSource à partir de l’URI d’un fichier manifeste HLS ou DASH. Ensuite, définissez la propriété Source de MediaPlayerElement. MediaPlayerElement crée automatiquement un objet MediaPlayer pour le contenu. Vous pouvez appeler Play sur MediaPlayer pour démarrer 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();

Notes

À compter de Windows 10, version 1607, nous vous recommandons d’utiliser la classe MediaPlayer pour lire des éléments multimédias. MediaPlayerElement est un contrôle XAML léger utilisé pour afficher le contenu d’un MediaPlayer dans une page XAML. Le contrôle MediaElement continue d’être pris en charge pour la compatibilité descendante. Pour plus d’informations sur l’utilisation de MediaPlayer et MediaPlayerElement pour lire du contenu multimédia, voir Lire du contenu audio et vidéo avec MediaPlayer. Pour plus d’informations sur l’utilisation de MediaSource et des API associées pour utiliser du contenu multimédia, voir Éléments, playlists et pistes multimédias.

Streaming adaptatif avec AdaptiveMediaSource

Si votre application a besoin de fonctionnalités de diffusion en continu adaptative plus avancées, telles que la fourniture d’en-têtes HTTP personnalisés, la surveillance des débits de téléchargement et de lecture actuels ou l’ajustement des ratios qui déterminent le moment où le système change de débit 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 des API des espaces de noms suivants.

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

Initialisez une source AdaptiveMediaSource à partir d’un URI.

Initialisez l’élément AdaptiveMediaSource avec l’URI d’un fichier manifeste de streaming adaptatif en appelant la méthode CreateFromUriAsync. La valeur AdaptiveMediaSourceCreationStatus retournée par cette méthode vous permet de savoir si la source multimé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 maximal pris en charge pour ce flux, puis cette valeur est définie comme débit initial. Cet exemple enregistre également des gestionnaires pour les différents événements AdaptiveMediaSource 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 un AdaptiveMediaSource à l’aide de HttpClient

Si vous avez besoin de définir des en-têtes HTTP personnalisés pour l’obtention du fichier manifeste, vous pouvez créer un objet HttpClient, définir les en-têtes souhaités, puis transmettre 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 à partir du serveur. La classe AdaptiveMediaSourceDownloadRequestedEventArgs transmise dans le gestionnaire d’événements 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 demande de ressource à l’aide de l’événement DownloadRequested

Vous pouvez utiliser le gestionnaire d’événements DownloadRequested pour modifier la demande de ressource en mettant à jour les propriétés de l’objet AdaptiveMediaSourceDownloadResult fourni par les arguments d’é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 de résultat. Vous pouvez également réécrire le décalage et la longueur de la plage d’octets pour les segments multimédias ou, comme illustré dans l’exemple ci-dessous, modifier l’URI de la ressource pour télécharger la ressource complète et définir le décalage et la longueur de la plage d’octets sur Null.

Vous pouvez remplacer le contenu de la ressource demandée en définissant les propriétés Buffer ou InputStream de l’objet résultat. Dans l’exemple ci-après, le contenu de la ressource de manifeste est remplacé par la définition de la propriété Buffer. Notez que si vous mettez à jour la demande de ressource avec des données obtenues en mode asynchrone, par exemple dans le cadre d’une récupération de données à partir d’un serveur distant ou d’une authentification utilisateur asynchrone, vous devez appeler AdaptiveMediaSourceDownloadRequestedEventArgs.GetDeferral pour obtenir un report, puis Complete une fois l’opération terminée pour signaler au système que l’opération de demande de téléchargement peut continuer.

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

Utiliser des événements de débit pour gérer et répondre aux changements de débit

L’objet AdaptiveMediaSource fournit des événements qui vous permettent de réagir lorsque les débits 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 le moment où 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 demandée échoue. Vous pouvez utiliser cet événement pour mettre à jour votre interface utilisateur en réponse à l’échec. Vous pouvez également utiliser l’événement pour consigner des informations statistiques sur l’opération de téléchargement et l’échec.

L’objet AdaptiveMediaSourceDownloadFailedEventArgs passé dans le gestionnaire d’événements contient des métadonnées relatives au téléchargement de la ressource ayant échoué, telles que le type de ressource, l’URI de la ressource et la position dans le flux où l’échec s’est produit. RequestId obtient un identificateur unique généré par le système pour la demande qui peut être utilisé pour mettre en corrélation status informations sur une requête individuelle sur 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 le minutage des différents jalons de l’opération de téléchargement. Vous pouvez enregistrer ces informations afin d’identifier les problèmes de perfomance dans votre implémentation de streaming adaptatif.

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 lorsqu’un téléchargement de ressource se termine et génère des données similaires à l’événement DownloadFailed. Une fois de plus, un RequestId est fourni pour la corrélation des événements pour une seule requête. En outre, un objet AdaptiveMediaSourceDownloadStatistics est fourni pour activer la journalisation 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);

}

Collecter des données de télémétrie de streaming adaptatif avec AdaptiveMediaSourceDiagnostics

AdaptiveMediaSource expose une propriété Diagnostics qui retourne un objet AdaptiveMediaSourceDiagnostics. Utilisez cet objet pour vous inscrire à l’événement DiagnosticAvailable . Cet événement est destiné à être utilisé pour la collecte de données de télémétrie 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 différentes raisons. 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é déclenché. Les raisons potentielles incluent les erreurs d’accès à la ressource demandée et les erreurs d’analyse du fichier manifeste de streaming. Pour obtenir la liste des situations qui peuvent déclencher un événement de diagnostic, consultez AdaptiveMediaSourceDiagnosticType. Comme les arguments pour d’autres événements de streaming adaptatif, AdaptiveMediaSourceDiagnosticAvailableEventArgs fournit un RequestId approprié pour la corrélation des informations de requête entre 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 streaming adaptatif pour les éléments d’une liste de lecture à l’aide de MediaBinder

La classe MediaBinder vous permet de différer la liaison de contenu multimédia dans un MediaPlaybackList. À compter de Windows 10, version 1703, vous pouvez fournir un AdaptiveMediaSource en tant que contenu lié. Le processus de liaison différée d’une source multimédia adaptative est en grande partie le même que la liaison d’autres types de média, qui est décrit dans Éléments multimédias, playlists et pistes.

Créez une instance MediaBinder, définissez une chaîne de jeton définie par l’application pour identifier le contenu à lier et inscrivez-vous à l’événement Binding. Créez un MediaSource à partir du classeur en appelant MediaSource.CreateFromMediaBinder. Ensuite, créez un élément MediaPlaybackItem à partir de 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 d’événements Binding , utilisez la chaîne de jeton pour identifier le contenu à lier, puis créez la source multimédia adaptative en appelant l’une des surcharges de CreateFromStreamAsync ou CreateFromUriAsync. Étant donné qu’il s’agit de méthodes asynchrones, vous devez d’abord appeler la méthode MediaBindingEventArgs.GetDeferral pour indiquer au système d’attendre que votre opération se termine avant de continuer. Définissez la source multimédia adaptative comme contenu lié en appelant SetAdaptiveMediaSource. Enfin, appelez Deferral.Complete une fois votre opération terminée pour indiquer 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 inscrire 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 MediaPlaybackList. La propriété CurrentMediaPlaybackItemChangedEventArgs.NewItem contient le nouveau MediaPlaybackItem en cours de lecture dans la liste. Obtenez une instance de l’AdaptiveMediaSource représentant le nouvel élément en accédant à la propriété Source de l’objet MediaPlaybackItem, puis à la propriété AdaptiveMediaSource de la source multimédia. Cette propriété a la valeur Null si le nouvel élément de lecture n’est pas un AdaptiveMediaSource. Vous devez donc tester la valeur null avant de tenter d’inscrire 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;
        }
    }
}