Lire du contenu audio et vidéo avec MediaPlayer

Cet article vous explique comment lire du contenu multimédia dans votre application Windows universelle avec la classe MediaPlayer. Avec Windows 10 version 1607, des améliorations significatives ont été apportées aux API de lecture multimédia, notamment une conception à processus unique simplifié pour l’audio en arrière-plan, l’intégration automatique avec les contrôles de transport multimédia système (SMTC), la possibilité de synchroniser plusieurs lecteurs multimédias, la possibilité de restituer des images vidéo sur une surface Windows.UI.Composition et une interface facile pour créer et planifier des interruptions multimédias dans votre contenu. Pour tirer parti de ces améliorations, la meilleure pratique recommandée pour la lecture de contenus multimédias consiste à utiliser la classe MediaPlayer en lieu et place de MediaElement pour la lecture de contenu multimédia. Le contrôle XAML léger, MediaPlayerElement, a été introduit pour vous permettre d’afficher le contenu multimédia dans une page XAML. Nombre des API de statut et de contrôle de la lecture fournies par MediaElement sont désormais disponibles via le nouvel objet MediaPlaybackSession. MediaElement continue de fonctionner pour prendre en charge la compatibilité descendante, mais aucune fonction supplémentaire ne sera ajoutée à cette classe.

Cet article vous présente les fonctions MediaPlayer qu’une application standard de lecture de contenu multimédia utilise. Notez que MediaPlayer utilise la classe MediaSource en tant que conteneur pour l’ensemble des éléments multimédias. Cette classe vous permet de charger et de lire le contenu multimédia à partir de multiples sources différentes utilisant une interface unique, notamment les fichiers locaux, les flux de mémoire et les sources réseau. Il existe également des classes de niveau supérieur compatibles avec MediaSource, comme MediaPlaybackItem et MediaPlaybackList, qui fournissent des fonctions plus avancées comme des playlists et la capacité de gestion de sources multimédias avec plusieurs pistes audio, vidéo et de métadonnées. Pour plus d’informations sur MediaSource et les API associées, consultez la page Éléments, playlists et pistes multimédias.

Notes

Windows 10 éditions N et Windows 10 KN n’incluent pas les fonctionnalités multimédias requises pour utiliser MediaPlayer pour la lecture. Ces fonctionnalités peuvent être installées manuellement. Pour plus d’informations, consultez Media Feature Pack pour Windows 10 éditions N et Windows 10 KN.

Lire un fichier multimédia avec MediaPlayer

La lecture de contenu multimédia de base avec MediaPlayer est très simple à implémenter. Tout d’abord, créez une nouvelle instance de la classe MediaPlayer. Votre application peut présenter plusieurs instances MediaPlayer actives simultanément. Ensuite, définissez la propriété Source du lecteur sur un objet qui implémente IMediaPlaybackSource, tel qu’un MediaSource, un MediaPlaybackItem ou un MediaPlaybackList. Dans cet exemple, un objet MediaSource est créé à partir d’un fichier dans le stockage local de l’application, puis un objet MediaPlaybackItem est créé à partir de la source, puis affecté à la propriété Source du lecteur.

Contrairement à l’objet MediaElement, MediaPlayer ne démarre pas automatiquement la lecture par défaut. Vous pouvez commencer la lecture en appelant Play, en définissant la propriété Lecture automatique sur true ou en attendant que l’utilisateur lance la lecture avec les contrôles multimédias intégrés.

mediaPlayer = new MediaPlayer();
mediaPlayer.Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video.mkv"));
mediaPlayer.Play();

Lorsque vous avez terminé d’utiliser une instance MediaPlayer sur l’application, vous devez appeler la méthode Close (projetée vers Dispose en C#) afin de nettoyer les ressources utilisées par le lecteur.

mediaPlayer.Dispose();

Utiliser MediaPlayerElement afin d’afficher du contenu vidéo dans XAML

Vous pouvez lire du contenu multimédia dans une instance MediaPlayer sans l’afficher au format XAML, mais de nombreuses applications de lecture de contenus multimédias sont définies pour ce type d’affichage. Pour ce faire, utilisez le contrôle léger MediaPlayerElement. Tout comme MediaElement, MediaPlayerElement vous permet de spécifier si les contrôles intégrés de transport doivent être affichés.

<MediaPlayerElement x:Name="_mediaPlayerElement" AreTransportControlsEnabled="False" HorizontalAlignment="Stretch"  Grid.Row="0"/>

Vous pouvez définir l’instance MediaPlayer à laquelle l’élément est lié en appelant SetMediaPlayer.

_mediaPlayerElement.SetMediaPlayer(mediaPlayer);

Vous pouvez également définir la source de lecture sur l’instance MediaPlayerElement. Le cas échéant, l’élément crée automatiquement une nouvelle instance MediaPlayer à laquelle vous pouvez accéder à l’aide de la propriété MediaPlayer.

_mediaPlayerElement.Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video.mkv"));
mediaPlayer = _mediaPlayerElement.MediaPlayer;
mediaPlayer.Play();

Notes

Si vous désactivez l’élément MediaPlaybackCommandManager de l’instance MediaPlayer en définissant IsEnabled sur false, le lien entre MediaPlayer et TransportControls fourni par MediaPlayerElement est rompu ; autrement dit, les contrôles de transport intégrés ne contrôleront plus automatiquement la lecture du lecteur. Vous devrez donc implémenter vos propres contrôles pour pouvoir contrôler le MediaPlayer.

Tâches courantes de MediaPlayer

Cette section vous explique comment utiliser certaines des fonctionnalités de l’instance MediaPlayer.

Définir la catégorie audio

Définissez la propriété AudioCategory d’une instance MediaPlayer sur l’une des valeurs de l’énumération MediaPlayerAudioCategory afin d’indiquer au système le type de contenu multimédia lu. Les jeux doivent classer leurs flux musicaux en tant qu’éléments GameMedia, de manière à ce que la musique du jeu soit désactivée automatiquement si de la musique s’active sur une autre application en arrière-plan. Les applications de musique ou de vidéo doivent classer leurs flux en tant qu’éléments Media ou Movie, de manière à les rendre prioritaires par rapport aux flux GameMedia.

mediaPlayer.AudioCategory = MediaPlayerAudioCategory.Media;

Sortie vers un point de terminaison audio spécifique

Par défaut, la sortie audio d’une instance MediaPlayer est acheminée vers le point de terminaison audio par défaut du système, mais vous pouvez définir un point de terminaison audio spécifique que l’instance MediaPlayer doit utiliser pour la sortie. Dans l’exemple ci-dessous, MediaDevice.GetAudioRenderSelector retourne une chaîne qui définit de manière unique la catégorie de rendu audio des appareils. Ensuite, la méthode DeviceInformation FindAllAsync est appelée pour obtenir une liste de tous les appareils disponibles du type sélectionné. Vous pouvez déterminer par programmation quel appareil vous souhaitez utiliser ou ajouter les appareils retournés à un objet ComboBox pour permettre à l’utilisateur de sélectionner un appareil.

string audioSelector = MediaDevice.GetAudioRenderSelector();
var outputDevices = await DeviceInformation.FindAllAsync(audioSelector);
foreach (var device in outputDevices)
{
    var deviceItem = new ComboBoxItem();
    deviceItem.Content = device.Name;
    deviceItem.Tag = device;
    _audioDeviceComboBox.Items.Add(deviceItem);
}

Dans l’événement SelectionChanged de la zone déroulante des appareils, la propriété AudioDevice de l’instance MediaPlayer est définie sur l’appareil sélectionné, qui était stocké dans la propriété Tag de l’instance ComboBoxItem.

private void _audioDeviceComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    DeviceInformation selectedDevice = (DeviceInformation)((ComboBoxItem)_audioDeviceComboBox.SelectedItem).Tag;
    if (selectedDevice != null)
    {
        mediaPlayer.AudioDevice = selectedDevice;
    }
}

Session de lecture

Comme décrit précédemment dans cet article, nombre des fonctions exposées par la classe MediaElement ont été déplacées vers la classe MediaPlaybackSession. Cela concerne les informations sur l’état de lecture du lecteur, comme la position actuelle de lecture, l’action du lecteur (pause ou lecture) et la vitesse de lecture actuelle. MediaPlaybackSession fournit également plusieurs événements vous procurant des informations sur les modifications de l’état, notamment sur le statut actuel de mise en mémoire tampon et de téléchargement du contenu lu et sur la taille naturelle et les proportions du contenu vidéo actuellement lu.

L’exemple suivant vous illustre l’implémentation d’un gestionnaire de clic du bouton qui permet d’avancer de 10 secondes dans le contenu. Tout d’abord, l’objet MediaPlaybackSession associé au lecteur est récupéré avec la propriété PlaybackSession. Ensuite, la propriété Position est définie sur la position actuelle de lecture plus 10 secondes.

private void _skipForwardButton_Click(object sender, RoutedEventArgs e)
{
    var session = mediaPlayer.PlaybackSession;
    session.Position = session.Position + TimeSpan.FromSeconds(10);
}

L’exemple suivant illustre l’utilisation d’un bouton bascule permettant de passer de la vitesse de lecture normale à la vitesse double, en définissant la propriété PlaybackRate de la session.

private void _speedToggleButton_Checked(object sender, RoutedEventArgs e)
{
    mediaPlayer.PlaybackSession.PlaybackRate = 2.0;
}
private void _speedToggleButton_Unchecked(object sender, RoutedEventArgs e)
{
    mediaPlayer.PlaybackSession.PlaybackRate = 1.0;
}

À compter de Windows 10 version 1803, vous pouvez définir la rotation avec laquelle la vidéo est présentée dans MediaPlayer par incréments de 90 degrés.

mediaPlayer.PlaybackSession.PlaybackRotation = MediaRotation.Clockwise90Degrees;

Détecter la mise en mémoire tampon attendue et inattendue

L’objet MediaPlaybackSession décrit dans la section précédente fournit deux événements pour détecter le début et la fin de la mise en mémoire tampon du fichier multimédia en cours de lecture , BufferingStarted et BufferingEnded. Cela vous permet de mettre à jour votre interface utilisateur pour montrer à l’utilisateur que la mise en mémoire tampon se produit. La mise en mémoire tampon initiale est attendue lors de la première ouverture d’un fichier multimédia ou lorsque l’utilisateur bascule vers un nouvel élément dans une playlist. Une mise en mémoire tampon inattendue peut se produire lorsque la vitesse du réseau se dégrade ou si le système de gestion de contenu qui fournit le contenu rencontre des problèmes techniques. À partir de RS3, vous pouvez utiliser l’événement BufferingStarted pour déterminer si l’événement de mise en mémoire tampon est attendu ou s’il est inattendu et interrompt la lecture. Vous pouvez utiliser ces informations comme données de télémétrie pour votre application ou votre service de distribution multimédia.

Inscrivez des gestionnaires pour les événements BufferingStarted et BufferingEnded pour recevoir des notifications d’état de mise en mémoire tampon.

mediaPlayer.PlaybackSession.BufferingStarted += MediaPlaybackSession_BufferingStarted;
mediaPlayer.PlaybackSession.BufferingEnded += MediaPlaybackSession_BufferingEnded;

Dans le gestionnaire d’événements BufferingStarted, castez les arguments d’événement passés dans l’événement à un objet MediaPlaybackSessionBufferingStartedEventArgs et case activée la propriété IsPlaybackInterruption. Si cette valeur est true, la mise en mémoire tampon qui a déclenché l’événement est inattendue et interrompt la lecture. Sinon, la mise en mémoire tampon initiale est attendue.

private void MediaPlaybackSession_BufferingStarted(MediaPlaybackSession sender, object args)
{
    MediaPlaybackSessionBufferingStartedEventArgs bufferingStartedEventArgs = args as MediaPlaybackSessionBufferingStartedEventArgs;
    if (bufferingStartedEventArgs != null && bufferingStartedEventArgs.IsPlaybackInterruption)
    {
        // update the playback quality telemetry report to indicate that
        // playback was interrupted
    }

    // update the UI to indicate that playback is buffering
}
private void MediaPlaybackSession_BufferingEnded(MediaPlaybackSession sender, object args)
{
    // update the UI to indicate that playback is no longer buffering
}

Pincement et zoom sur du contenu vidéo

MediaPlayer vous permet de spécifier le rectangle source au sein du contenu vidéo à afficher. Dès lors, vous pouvez effectuer un zoom dans la vidéo. Le rectangle que vous spécifiez est relatif à un rectangle normalisé (0,0,1,1), où 0,0 correspond au coin supérieur gauche de l’image et 1,1 spécifie la largeur et la hauteur complètes de l’image. Ainsi, par exemple, pour définir le rectangle de zoom de manière à afficher le quadrant supérieur droit de la vidéo, vous définiriez le rectangle (0,5 ; 0 ; 0,5 ; 0,5). Il est important que vous vérifiiez vos valeurs afin de vous assurer que votre rectangle source se trouve dans le rectangle normalisé (0,0,1,1). Toute tentative de définition de la valeur en dehors de cette plage provoquera l’envoi d’une exception.

Pour implémenter les fonctionnalités de pincement et de zoom à l’aide d’entrées tactiles multipoint, vous devez dans un premier temps spécifier les entrées prises en charge. Dans cet exemple, les entrées de mise à l’échelle et de translation sont demandées. L’événement ManipulationDelta est déclenché lorsque l’une des entrées enregistrées se produit. L’événement DoubleTapped sera utilisé pour réinitialiser le zoom sur le plein format.

_mediaPlayerElement.ManipulationMode = ManipulationModes.Scale | ManipulationModes.TranslateX | ManipulationModes.TranslateY;
_mediaPlayerElement.ManipulationDelta += _mediaPlayerElement_ManipulationDelta;
_mediaPlayerElement.DoubleTapped += _mediaPlayerElement_DoubleTapped;

Ensuite, déclarez un objet Rect qui stockera le rectangle source actuel de zoom.

Rect _sourceRect = new Rect(0, 0, 1, 1);

Le gestionnaire ManipulationDelta ajuste la mise à l’échelle ou la translation du rectangle de zoom. Si la valeur delta de mise à l’échelle est différente de 1, cela signifie que l’utilisateur à effectuer un pincement. Si la valeur est supérieure à 1, le rectangle source doit être réduit pour la prise en charge du zoom dans le contenu. Si la valeur est inférieure à 1, le rectangle source doit être agrandi pour effectuer un zoom arrière. Avant de définir les nouvelles valeurs d’échelle, le rectangle résultant est vérifié pour s’assurer qu’il se trouve entièrement dans les limites (0,0,1,1).

Si la valeur de mise à l’échelle est 1, l’entrée de translation est traitée. Le rectangle est simplement translaté selon la division du nombre de pixels de l’entrée par la valeur de largeur et de hauteur du contrôle. Là encore, le rectangle obtenu est examiné afin de garantir qu’il s’intègre parfaitement dans les limites (0,0,1,1).

Enfin, l’élément NormalizedSourceRect de l’instance MediaPlaybackSession est défini sur le nouveau rectangle ajusté ; il spécifie la zone de la vidéo à afficher.

private void _mediaPlayerElement_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{

    if (e.Delta.Scale != 1)
    {
        var halfWidth = _sourceRect.Width / 2;
        var halfHeight = _sourceRect.Height / 2;

        var centerX = _sourceRect.X + halfWidth;
        var centerY = _sourceRect.Y + halfHeight;

        var scale = e.Delta.Scale;
        var newHalfWidth = (_sourceRect.Width * e.Delta.Scale) / 2;
        var newHalfHeight = (_sourceRect.Height * e.Delta.Scale) / 2;

        if (centerX - newHalfWidth > 0 && centerX + newHalfWidth <= 1.0 &&
            centerY - newHalfHeight > 0 && centerY + newHalfHeight <= 1.0)
        {
            _sourceRect.X = centerX - newHalfWidth;
            _sourceRect.Y = centerY - newHalfHeight;
            _sourceRect.Width *= e.Delta.Scale;
            _sourceRect.Height *= e.Delta.Scale;
        }
    }
    else
    {
        var translateX = -1 * e.Delta.Translation.X / _mediaPlayerElement.ActualWidth;
        var translateY = -1 * e.Delta.Translation.Y / _mediaPlayerElement.ActualHeight;

        if (_sourceRect.X + translateX >= 0 && _sourceRect.X + _sourceRect.Width + translateX <= 1.0 &&
            _sourceRect.Y + translateY >= 0 && _sourceRect.Y + _sourceRect.Height + translateY <= 1.0)
        {
            _sourceRect.X += translateX;
            _sourceRect.Y += translateY;
        }
    }

    mediaPlayer.PlaybackSession.NormalizedSourceRect = _sourceRect;
}

Dans le gestionnaire d’événements DoubleTapped , le rectangle source est rétabli sur (0,0,1,1) pour provoquer le rendu de l’image vidéo entière.

private void _mediaPlayerElement_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{
    _sourceRect = new Rect(0, 0, 1, 1);
    mediaPlayer.PlaybackSession.NormalizedSourceRect = _sourceRect;
}

NOTE Cette section décrit l’entrée tactile. Le pavé tactile envoie des événements de pointeur et n’envoie pas d’événements manipulation.

Gestion de la dégradation de la lecture basée sur des stratégies

Dans certaines circonstances, le système peut dégrader la lecture d’un élément multimédia, par exemple en réduisant la résolution (constriction), en fonction d’une stratégie plutôt que d’un problème de performances. Par exemple, la vidéo peut être dégradée par le système si elle est lue à l’aide d’un pilote vidéo non signé. Vous pouvez appeler MediaPlaybackSession.GetOutputDegradationPolicyState pour déterminer si et pourquoi cette dégradation basée sur une stratégie se produit et alerter l’utilisateur ou enregistrer la raison de la télémétrie.

L’exemple suivant montre une implémentation d’un gestionnaire pour l’événement MediaPlayer.MediaOpened qui est déclenché lorsque le lecteur ouvre un nouvel élément multimédia. GetOutputDegradationPolicyState est appelé sur le MediaPlayer passé dans le gestionnaire. La valeur de VideoConstrictionReason indique la raison de la stratégie pour laquelle la vidéo est constrictée. Si la valeur n’est pas None, cet exemple consigne la raison de la dégradation à des fins de télémétrie. Cet exemple montre également la définition du débit binaire d’AdaptiveMediaSource en cours de lecture sur la bande passante la plus faible pour économiser l’utilisation des données, car la vidéo est limitée et ne sera pas affichée en haute résolution. Pour plus d’informations sur l’utilisation d’AdaptiveMediaSource, consultez Diffusion en continu adaptative.

private void MediaPlayer_MediaOpened(MediaPlayer sender, object args)
{
    MediaPlaybackSessionOutputDegradationPolicyState info = sender.PlaybackSession.GetOutputDegradationPolicyState();

    if (info.VideoConstrictionReason != MediaPlaybackSessionVideoConstrictionReason.None)
    {
        // Switch to lowest bitrate to save bandwidth
        adaptiveMediaSource.DesiredMaxBitrate = adaptiveMediaSource.AvailableBitrates[0];

        // Log the degradation reason or show a message to the user
        System.Diagnostics.Debug.WriteLine("Logging constriction reason: " + info.VideoConstrictionReason);
    }
}

Utilisez MediaPlayerSurface afin d’afficher la vidéo sur une surface Windows.UI.Composition

À partir de Windows 10, version 1607, vous pouvez utiliser l’instance MediaPlayer afin d’afficher le contenu vidéo sur une interface ICompositionSurface, ce qui permet au lecteur d’intergir avec les API dans l’espace de noms Windows.UI.Composition. L’infrastructure de composition peut être mise à profit pour travailler avec des graphiques dans la couche visuelle située entre les API XAML et les API graphiques DirectX de niveau inférieur. Cela permet la prise en charge de scénarios tels que le rendu de contenus vidéo dans tout contrôle XAML. Pour plus d’informations sur l’utilisation des API de composition, consultez la page Couche visuelle.

L’exemple suivant illustre l’affichage d’un contenu de lecteur vidéo sur un contrôle Canvas. Les appels spécifiques au lecteur multimédias de cet exemple sont SetSurfaceSize et GetSurface. SetSurfaceSize indique au système la talle de la mémoire tampon à allouer pour l’affichage du contenu. GetSurface prend un élément Compositor en tant qu’argument et récupère une instance de la classe MediaPlayerSurface. Cette classe procure un accès aux éléments MediaPlayer et Compositor utilisés pour créer la surface et l’expose via la propriété CompositionSurface.

Le reste du code de cet exemple crée un SpriteVisual dans lequel la vidéo est rendue et définit la taille de l’élément de canevas qui affichera le visuel. Ensuite, un élément CompositionBrush est créé à partir de la classe MediaPlayerSurface et affecté à la propriété Brush des éléments visuels. À ce stade, un élément ContainerVisual est créé et l’instance SpriteVisual est insérée dans la partie supérieure de son arborescence visuelle. Enfin, SetElementChildVisual est appelé afin d’affecter les éléments visuels du conteneur au contrôle Canvas.

mediaPlayer.SetSurfaceSize(new Size(_compositionCanvas.ActualWidth, _compositionCanvas.ActualHeight));

var compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
MediaPlayerSurface surface = mediaPlayer.GetSurface(compositor);

SpriteVisual spriteVisual = compositor.CreateSpriteVisual();
spriteVisual.Size =
    new System.Numerics.Vector2((float)_compositionCanvas.ActualWidth, (float)_compositionCanvas.ActualHeight);

CompositionBrush brush = compositor.CreateSurfaceBrush(surface.CompositionSurface);
spriteVisual.Brush = brush;

ContainerVisual container = compositor.CreateContainerVisual();
container.Children.InsertAtTop(spriteVisual);

ElementCompositionPreview.SetElementChildVisual(_compositionCanvas, container);

Utilisez MediaTimelineController afin de synchroniser du contenu entre plusieurs couches.

Comme indiqué précédemment dans cet article, vous application peut disposer de plusieurs objets MediaPlayer actifs simultanément. Par défaut, chaque instance MediaPlayer créée fonctionne indépendamment. Pour certains scénarios, tels que la synchronisation d’une piste de commentaires sur une vidéo, vous voudrez synchroniser l’état du lecteur, la position de lecture et la vitesse de lecture de plusieurs couches. À partir de Windows 10, version 1607, vous pouvez implémenter ce comportement à l’aide de la classe MediaTimelineController.

Implémenter des contrôles de lecture

L’exemple suivant vous explique l’utilisation d’une classe MediaTimelineController pour contrôler deux instances de MediaPlayer. Tout d’abord, chaque instance de MediaPlayer est instanciée et l’objet Source est défini sur un fichier multimédia. Ensuite, une nouvelle classe MediaTimelineController est créée. Pour chaque instance MediaPlayer, la classe MediaPlaybackCommandManager associée à chaque lecteur est désactivée par la définition de la propriété IsEnabled sur False. Ensuite, la propriété TimelineController est définie sur l’objet contrôleur chronologie.

MediaTimelineController _mediaTimelineController;
mediaPlayer = new MediaPlayer();
mediaPlayer.Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video.mkv"));
_mediaPlayerElement.SetMediaPlayer(mediaPlayer);


_mediaPlayer2 = new MediaPlayer();
_mediaPlayer2.Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video_2.mkv"));
_mediaPlayerElement2.SetMediaPlayer(_mediaPlayer2);

_mediaTimelineController = new MediaTimelineController();

mediaPlayer.CommandManager.IsEnabled = false;
mediaPlayer.TimelineController = _mediaTimelineController;

_mediaPlayer2.CommandManager.IsEnabled = false;
_mediaPlayer2.TimelineController = _mediaTimelineController;

Attention La classe MediaPlaybackCommandManager procure une intégration automatique entre MediaPlayer et les contrôles de transport de média système, mais cette intégration automatique ne peut pas être utilisée avec des lecteurs multimédias contrôlés avec une classe MediaTimelineController. Par conséquent, vous devez désactiver le gestionnaire de commande du lecteur multimédia avant de définir son contrôleur de chronologie. Si vous ne le faites pas, une exception est levée avec le message suivant : « L’attachement du contrôleur de chronologie de média est bloqué en raison de l’état actuel de l’objet ». Pour plus d’informations sur l’intégration du lecteur multimédia au SMTC, consultez Intégrer aux contrôles de transport de média système. Si vous utilisez une classe MediaTimelineController, vous pouvez toujours contrôler manuellement les contrôles de transport de média système. Pour plus d’informations, consultez la page Contrôles de transport de média système.

Une fois que vous avez associé une classe MediaTimelineController à un ou plusieurs lecteurs multimédias, vous pouvez contrôler l’état de lecture à l’aide des méthodes exposées par le contrôleur. L’exemple suivant appelle Start pour commencer la lecture de tous les lecteurs multimédias associés au début du média.

private void PlayButton_Click(object sender, RoutedEventArgs e)
{
    _mediaTimelineController.Start();
}

Cet exemple illustre l’interruption et la reprise de la lecture sur l’ensemble des lecteurs multimédias associés.

private void PauseButton_Click(object sender, RoutedEventArgs e)
{
    if(_mediaTimelineController.State == MediaTimelineControllerState.Running)
    {
        _mediaTimelineController.Pause();
        _pauseButton.Content = "Resume";
    }
    else
    {
        _mediaTimelineController.Resume();
        _pauseButton.Content = "Pause";
    }
}

Pour définir l’avance rapide sur l’ensemble des lecteurs multimédias connectés, définissez la vitesse de lecture sur une valeur supérieure à 1.

private void FastForwardButton_Click(object sender, RoutedEventArgs e)
{
    _mediaTimelineController.ClockRate = 2.0;
}

L’exemple suivant illustre l’utilisation d’un contrôle Slider afin d’afficher la position de lecture actuelle du contrôleur de chronologie par rapport à la durée du contenu sur l’un des lecteurs multimédias associés. Tout d’abord, un nouvel élément MediaSource est créé et un gestionnaire est enregistré pour l’événement OpenOperationCompleted de la source multimédia.

var mediaSource = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video.mkv"));
mediaSource.OpenOperationCompleted += MediaSource_OpenOperationCompleted;
mediaPlayer.Source = mediaSource;
_mediaPlayerElement.SetMediaPlayer(mediaPlayer);

Le gestionnaire OpenOperationCompleted est utilisé en tant qu’opportunité de découverte de la durée du contenu de la source multimédia. Une fois que la durée est déterminée, la valeur maximale du contrôle Slider est définie sur le nombre total de secondes de l’élément multimédia. La valeur est définie au sein d’un appel à RunAsync, afin de garantir l’exécution sur le thread d’interface utilisateur.

TimeSpan _duration;
private async void MediaSource_OpenOperationCompleted(MediaSource sender, MediaSourceOpenOperationCompletedEventArgs args)
{
    _duration = sender.Duration.GetValueOrDefault();

    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        _positionSlider.Minimum = 0;
        _positionSlider.Maximum = _duration.TotalSeconds;
        _positionSlider.StepFrequency = 1;
    }); 
}

Ensuite, un gestionnaire pour l’événement PositionChanged du contrôleur de chronologie est enregistré. L’appel est effectué régulièrement par le système, environ 4 fois par seconde.

_mediaTimelineController.PositionChanged += _mediaTimelineController_PositionChanged;

Dans le gestionnaire de PositionChanged, la valeur Slider est mise à jour en fonction de la position actuelle du contrôleur de chronologie.

private async void _mediaTimelineController_PositionChanged(MediaTimelineController sender, object args)
{
    if (_duration != TimeSpan.Zero)
    {
        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
        {
            _positionSlider.Value = sender.Position.TotalSeconds / (float)_duration.TotalSeconds;
        });
    }
}

Décaler la position de lecture de la position de la chronologie

Dans certains cas, il est possible que vous vouliez décaler la position de lecture d’un ou de plusieurs lecteurs multimédias des autres lecteurs. Pour ce faire, définissez la propriété TimelineControllerPositionOffset de l’objet MediaPlayer que vous voulez décaler. L’exemple suivant utilise les durées du contenu de deux lecteurs multimédias pour définir les valeurs minimum et maximum du contrôle à deux curseurs sur « plus » ou « moins » la longueur de l’élément.

_timelineOffsetSlider1.Minimum = -1 * _duration.TotalSeconds;
_timelineOffsetSlider1.Maximum = _duration.TotalSeconds;
_timelineOffsetSlider1.StepFrequency = 1;

_timelineOffsetSlider2.Minimum = -1 * _duration2.TotalSeconds;
_timelineOffsetSlider2.Maximum = _duration2.TotalSeconds;
_timelineOffsetSlider2.StepFrequency = 1;

Dans l’événement ValueChanged associé à chaque curseur, la propriété TimelineControllerPositionOffset de chaque lecteur est définie sur la valeur correspondante.

private void _timelineOffsetSlider1_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
{
    mediaPlayer.TimelineControllerPositionOffset = TimeSpan.FromSeconds(_timelineOffsetSlider1.Value);
}

private void _timelineOffsetSlider2_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
{
    _mediaPlayer2.TimelineControllerPositionOffset = TimeSpan.FromSeconds(_timelineOffsetSlider2.Value);
}

Notez que si la valeur de décalage d’un lecteur correspond à une position de lecture négative, le contenu demeure interrompu jusqu’à ce que le décalage atteigne la valeur de 0, puis la lecture redémarre. De la même manière, si la valeur de décalage correspond à une position de lecture supérieure à la durée de l’élément multimédia, l’image finale s’affiche, comme cela se produit à la fin de la lecture du contenu d’un lecteur multimédia.

Lire une vidéo sphérique avec MediaPlayer

À compter de Windows 10, version 1703, MediaPlayer prend en charge la projection équirectangulaire pour la lecture vidéo sphérique. Le contenu vidéo sphérique n’est pas différent de la vidéo plate normale dans la mesure où MediaPlayer restitue la vidéo tant que l’encodage vidéo est pris en charge. Pour la vidéo sphérique qui contient une balise de métadonnées spécifiant que la vidéo utilise une projection équirectangulaire, MediaPlayer peut restituer la vidéo à l’aide d’un champ de vision et d’une orientation de vue spécifiés. Cela permet des scénarios tels que la lecture de vidéos de réalité virtuelle avec un écran monté sur la tête ou simplement permettre à l’utilisateur de parcourir le contenu vidéo sphérique à l’aide de l’entrée de la souris ou du clavier.

Pour lire des vidéos sphériques, suivez les étapes de lecture du contenu vidéo décrites précédemment dans cet article. Une étape supplémentaire consiste à inscrire un gestionnaire pour l’événement MediaPlayer.MediaOpened . Cet événement vous donne la possibilité d’activer et de contrôler les paramètres de lecture vidéo sphérique.

mediaPlayer = new MediaPlayer();
mediaPlayer.MediaOpened += _mediaPlayer_MediaOpened;
mediaPlayer.Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video_spherical.mp4"));
_mediaPlayerElement.SetMediaPlayer(mediaPlayer);
mediaPlayer.Play();

Dans le gestionnaire MediaOpened, commencez par case activée le format d’image de l’élément multimédia nouvellement ouvert en vérifiant la propriété PlaybackSession.SphericalVideoProjection.FrameFormat. Si cette valeur est SphericaVideoFrameFormat.Equirectangular, le système peut projeter automatiquement le contenu vidéo. Tout d’abord, définissez la propriété PlaybackSession.SphericalVideoProjection.IsEnabled sur true. Vous pouvez également ajuster des propriétés telles que l’orientation de l’affichage et le champ de vision que le lecteur multimédia utilisera pour projeter le contenu vidéo. Dans cet exemple, le champ de vue est défini sur une valeur large de 120 degrés en définissant la propriété HorizontalFieldOfViewInDegrees .

Si le contenu vidéo est sphérique, mais dans un format autre que équirectangulaire, vous pouvez implémenter votre propre algorithme de projection à l’aide du mode serveur d’images du lecteur multimédia pour recevoir et traiter des images individuelles.

private void _mediaPlayer_MediaOpened(MediaPlayer sender, object args)
{
    if (sender.PlaybackSession.SphericalVideoProjection.FrameFormat == SphericalVideoFrameFormat.Equirectangular)
    {
        sender.PlaybackSession.SphericalVideoProjection.IsEnabled = true;
        sender.PlaybackSession.SphericalVideoProjection.HorizontalFieldOfViewInDegrees = 120;

    }
    else if (sender.PlaybackSession.SphericalVideoProjection.FrameFormat == SphericalVideoFrameFormat.Unsupported)
    {
        // If the spherical format is unsupported, you can use frame server mode to implement a custom projection
    }
}

L’exemple de code suivant montre comment ajuster l’orientation de l’affichage vidéo sphérique à l’aide des touches de direction gauche et droite.

protected override void OnKeyDown(KeyRoutedEventArgs e)
{
    if (mediaPlayer.PlaybackSession.SphericalVideoProjection.FrameFormat != SphericalVideoFrameFormat.Equirectangular)
    {
        return;
    }

    switch (e.Key)
    {
        case Windows.System.VirtualKey.Right:
            mediaPlayer.PlaybackSession.SphericalVideoProjection.ViewOrientation *= Quaternion.CreateFromYawPitchRoll(.1f, 0, 0);
            break;
        case Windows.System.VirtualKey.Left:
            mediaPlayer.PlaybackSession.SphericalVideoProjection.ViewOrientation *= Quaternion.CreateFromYawPitchRoll(-.1f, 0, 0);
            break;
    }
}

Si votre application prend en charge les playlists de vidéos, vous pouvez identifier les éléments de lecture qui contiennent des vidéos sphériques dans votre interface utilisateur. Les playlists multimédias sont décrites en détail dans l’article Éléments multimédias, playlists et pistes. L’exemple suivant montre la création d’une playlist, l’ajout d’un élément et l’inscription d’un gestionnaire pour l’événement MediaPlaybackItem.VideoTracksChanged , qui se produit lorsque les pistes vidéo d’un élément multimédia sont résolues.

var playbackList = new MediaPlaybackList();
var item = new MediaPlaybackItem(MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/RIFTCOASTER HD_injected.mp4")));
item.VideoTracksChanged += Item_VideoTracksChanged;
playbackList.Items.Add(item);
mediaPlayer.Source = playbackList;

Dans le gestionnaire d’événements VideoTracksChanged , obtenez les propriétés d’encodage des pistes vidéo ajoutées en appelant VideoTrack.GetEncodingProperties. Si la propriété SphericalVideoFrameFormat des propriétés d’encodage est une valeur autre que SphericaVideoFrameFormat.None, la piste vidéo contient une vidéo sphérique et vous pouvez mettre à jour votre interface utilisateur en conséquence si vous le souhaitez.

private void Item_VideoTracksChanged(MediaPlaybackItem sender, IVectorChangedEventArgs args)
{
    if (args.CollectionChange != CollectionChange.ItemInserted)
    {
        return;
    }
    foreach (var videoTrack in sender.VideoTracks)
    {
        if (videoTrack.GetEncodingProperties().SphericalVideoFrameFormat != SphericalVideoFrameFormat.None)
        {
            // Optionally indicate in the UI that this item contains spherical video
        }
    }
}

Utiliser MediaPlayer en mode serveur frame

À compter de Windows 10, version 1703, vous pouvez utiliser MediaPlayer en mode serveur frame. Dans ce mode, MediaPlayer ne restitue pas automatiquement les images à un élément MediaPlayerElement associé. Au lieu de cela, votre application copie le frame actuel de MediaPlayer vers un objet qui implémente IDirect3DSurface. Le scénario principal activé par cette fonctionnalité est l’utilisation de nuanceurs de pixels pour traiter les images vidéo fournies par MediaPlayer. Votre application est chargée d’afficher chaque image après le traitement, par exemple en affichant l’image dans un contrôle Image XAML.

Dans l’exemple suivant, un nouveau MediaPlayer est initialisé et le contenu vidéo est chargé. Ensuite, un gestionnaire pour VideoFrameAvailable est inscrit. Le mode serveur Frame est activé en définissant la propriété IsVideoFrameServerEnabled de l’objet MediaPlayer sur true. Enfin, la lecture multimédia est démarrée avec un appel à Play.

mediaPlayer = new MediaPlayer();
mediaPlayer.Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video.mkv"));
mediaPlayer.VideoFrameAvailable += mediaPlayer_VideoFrameAvailable;
mediaPlayer.IsVideoFrameServerEnabled = true;
mediaPlayer.Play();

L’exemple suivant montre un gestionnaire pour VideoFrameAvailable qui utilise Win2D pour ajouter un effet de flou simple à chaque image d’une vidéo, puis affiche les images traitées dans un contrôle Image XAML.

Chaque fois que le gestionnaire VideoFrameAvailable est appelé, la méthode CopyFrameToVideoSurface est utilisée pour copier le contenu de l’image dans un IDirect3DSurface. Vous pouvez également utiliser CopyFrameToStereoscopicVideoSurfaces pour copier du contenu 3D dans deux surfaces, afin de traiter séparément le contenu de l’œil gauche et de l’œil droit. Pour obtenir un objet qui implémente IDirect3DSurface , cet exemple crée un SoftwareBitmap , puis utilise cet objet pour créer un CanvasBitmap Win2D, qui implémente l’interface nécessaire. Un CanvasImageSource est un objet Win2D qui peut être utilisé comme source pour un contrôle Image . Par conséquent, un nouvel objet est créé et défini comme source de l’image dans laquelle le contenu sera affiché. Ensuite, un CanvasDrawingSession est créé. Il est utilisé par Win2D pour afficher l’effet de flou.

Une fois que tous les objets nécessaires ont été instanciés, CopyFrameToVideoSurface est appelé, ce qui copie l’image actuelle de MediaPlayer dans canvasBitmap. Ensuite, un GaussianBlurEffect Win2D est créé, avec canvasBitmap défini comme source de l’opération. Enfin, CanvasDrawingSession.DrawImage est appelé pour dessiner l’image source, avec l’effet de flou appliqué, dans le CanvasImageSource qui a été associé au contrôle Image , ce qui entraîne son dessin dans l’interface utilisateur.

private async void mediaPlayer_VideoFrameAvailable(MediaPlayer sender, object args)
{
    CanvasDevice canvasDevice = CanvasDevice.GetSharedDevice();

    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        if(frameServerDest == null)
        {
            // FrameServerImage in this example is a XAML image control
            frameServerDest = new SoftwareBitmap(BitmapPixelFormat.Rgba8, (int)FrameServerImage.Width, (int)FrameServerImage.Height, BitmapAlphaMode.Ignore);
        }
        if(canvasImageSource == null)
        {
            canvasImageSource = new CanvasImageSource(canvasDevice, (int)FrameServerImage.Width, (int)FrameServerImage.Height, DisplayInformation.GetForCurrentView().LogicalDpi);//96); 
            FrameServerImage.Source = canvasImageSource;
        }

        using (CanvasBitmap inputBitmap = CanvasBitmap.CreateFromSoftwareBitmap(canvasDevice, frameServerDest))
        using (CanvasDrawingSession ds = canvasImageSource.CreateDrawingSession(Windows.UI.Colors.Black))
        {

            mediaPlayer.CopyFrameToVideoSurface(inputBitmap);

            var gaussianBlurEffect = new GaussianBlurEffect
            {
                Source = inputBitmap,
                BlurAmount = 5f,
                Optimization = EffectOptimization.Speed
            };

            ds.DrawImage(gaussianBlurEffect);

        }
    });
}

private void FrameServerSubtitlesButton_Click(object sender, RoutedEventArgs e)
{

    mediaPlayer = new MediaPlayer();
    var source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video.mkv"));
    var item = new MediaPlaybackItem(source);

    item.TimedMetadataTracksChanged += Item_TimedMetadataTracksChanged;


    mediaPlayer.Source = item;
    mediaPlayer.VideoFrameAvailable += mediaPlayer_VideoFrameAvailable_Subtitle;
    mediaPlayer.IsVideoFrameServerEnabled = true;
    mediaPlayer.Play();

    mediaPlayer.IsMuted = true;

}

private void Item_TimedMetadataTracksChanged(MediaPlaybackItem sender, IVectorChangedEventArgs args)
{
    if(sender.TimedMetadataTracks.Count > 0)
    {
        sender.TimedMetadataTracks.SetPresentationMode(0, TimedMetadataTrackPresentationMode.PlatformPresented);
    }
}

private async void mediaPlayer_VideoFrameAvailable_Subtitle(MediaPlayer sender, object args)
{
    CanvasDevice canvasDevice = CanvasDevice.GetSharedDevice();

    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        if (frameServerDest == null)
        {
            // FrameServerImage in this example is a XAML image control
            frameServerDest = new SoftwareBitmap(BitmapPixelFormat.Rgba8, (int)FrameServerImage.Width, (int)FrameServerImage.Height, BitmapAlphaMode.Ignore);
        }
        if (canvasImageSource == null)
        {
            canvasImageSource = new CanvasImageSource(canvasDevice, (int)FrameServerImage.Width, (int)FrameServerImage.Height, DisplayInformation.GetForCurrentView().LogicalDpi);//96); 
            FrameServerImage.Source = canvasImageSource;
        }

        using (CanvasBitmap inputBitmap = CanvasBitmap.CreateFromSoftwareBitmap(canvasDevice, frameServerDest))
        {
            using (CanvasDrawingSession ds = canvasImageSource.CreateDrawingSession(Windows.UI.Colors.Black))
            {

                mediaPlayer.CopyFrameToVideoSurface(inputBitmap);

                //Rect subtitleTargetRect = new Rect(0, 0, inputBitmap.Bounds.Width, inputBitmap.Bounds.Bottom * .1);
                Rect subtitleTargetRect = new Rect(0, 0, 100, 100);

                mediaPlayer.RenderSubtitlesToSurface(inputBitmap);//, subtitleTargetRect);

                //var gaussianBlurEffect = new GaussianBlurEffect
                //{
                //    Source = inputBitmap,
                //    BlurAmount = 5f,
                //    Optimization = EffectOptimization.Speed
                //};

                //ds.DrawImage(gaussianBlurEffect);

                ds.DrawImage(inputBitmap);
            }
        }
    });
}

Pour plus d’informations sur Win2D, consultez le référentiel GitHub Win2D. Pour essayer l’exemple de code ci-dessus, vous devez ajouter le package NuGet Win2D à votre projet en suivant les instructions suivantes.

Pour ajouter le package NuGet Win2D à votre projet d’effet

  1. Dans l’Explorateur de solutions, cliquez avec le bouton droit sur le projet, puis sélectionnez Gérer les packages NuGet.
  2. En haut de la fenêtre, sélectionnez l’onglet Explorer.
  3. Dans la zone de recherche, entrez Win2D.
  4. Sélectionnez Win2D.uwp, puis Installer dans le volet droit.
  5. La boîte de dialogue Examiner les modifications vous indique le package à installer. Cliquez sur OK.
  6. Acceptez la licence de package.

Détecter et répondre aux changements de niveau audio par le système

À compter de Windows 10 version 1803, votre application peut détecter quand le système abaisse ou désactive le niveau audio d’un MediaPlayer en cours de lecture. Par exemple, le système peut abaisser, ou « canard », le niveau de lecture audio lorsqu’une alarme sonne. Le système désactive votre application lorsqu’elle passe en arrière-plan si celle-ci n’a pas déclaré la fonctionnalité backgroundMediaPlayback dans le manifeste de l’application. La classe AudioStateMonitor vous permet de vous inscrire pour recevoir un événement lorsque le système modifie le volume d’un flux audio. Accédez à la propriété AudioStateMonitor d’un MediaPlayer et inscrivez un gestionnaire pour l’événement SoundLevelChanged afin d’être averti lorsque le niveau audio de ce MediaPlayer est modifié par le système.

mediaPlayer.AudioStateMonitor.SoundLevelChanged += AudioStateMonitor_SoundLevelChanged;

Lorsque vous gérez l’événement SoundLevelChanged , vous pouvez effectuer différentes actions en fonction du type de contenu lu. Si vous jouez actuellement de la musique, vous voudrez peut-être laisser la musique continuer à jouer pendant que le volume est éclipsé. Toutefois, si vous lisez un podcast, vous souhaitez probablement suspendre la lecture pendant que l’audio est mis en échec afin que l’utilisateur ne manque aucun contenu.

Cet exemple déclare une variable pour déterminer si le contenu en cours de lecture est un podcast. Il est supposé que vous définissez cette valeur sur la valeur appropriée lors de la sélection du contenu pour MediaPlayer. Nous créons également une variable de classe pour suivre quand nous suspendons la lecture par programmation lorsque le niveau audio change.

bool isPodcast;
bool isPausedDueToAudioStateMonitor;

Dans le gestionnaire d’événements SoundLevelChanged, case activée la propriété SoundLevel de l’expéditeur AudioStateMonitor pour déterminer le nouveau niveau sonore. Cet exemple vérifie si le nouveau niveau sonore est plein volume, ce qui signifie que le système a cessé de désactiver ou de réduire le volume, ou si le niveau sonore a été abaissé, mais qu’il lit du contenu non podcast. Si l’une ou l’autre de ces valeurs est vraie et que le contenu a été précédemment suspendu par programmation, la lecture reprend. Si le nouveau niveau sonore est désactivé ou si le contenu actuel est un podcast et que le niveau sonore est faible, la lecture est suspendue et la variable est définie pour suivre que la pause a été lancée par programmation.

private void AudioStateMonitor_SoundLevelChanged(Windows.Media.Audio.AudioStateMonitor sender, object args)
{
    if ((sender.SoundLevel == SoundLevel.Full) || (sender.SoundLevel == SoundLevel.Low && !isPodcast))
    {
        if (isPausedDueToAudioStateMonitor)
        {
            mediaPlayer.Play();
            isPausedDueToAudioStateMonitor = false;
        }
    }
    else if ((sender.SoundLevel == SoundLevel.Muted) ||
         (sender.SoundLevel == SoundLevel.Low && isPodcast))
    {
        if (mediaPlayer.PlaybackSession.PlaybackState == MediaPlaybackState.Playing)
        {
            mediaPlayer.Pause();
            isPausedDueToAudioStateMonitor = true;
        }
    }

}

L’utilisateur peut décider de suspendre ou de poursuivre la lecture, même si l’audio est mis en pause par le système. Cet exemple montre des gestionnaires d’événements pour un bouton de lecture et de pause. Dans le bouton pause, le gestionnaire de clics est suspendu. Si la lecture a déjà été interrompue par programmation, nous mettons à jour la variable pour indiquer que l’utilisateur a suspendu le contenu. Dans le gestionnaire de clics du bouton lecture, nous reprenons la lecture et nous désactivons notre variable de suivi.

private void PauseButton_User_Click(object sender, RoutedEventArgs e)
{
    if (isPausedDueToAudioStateMonitor)
    {
        isPausedDueToAudioStateMonitor = false;
    }
    else
    {
        mediaPlayer.Pause();
    }
}

public void PlayButton_User_Click()
{
    isPausedDueToAudioStateMonitor = false;
    mediaPlayer.Play();
}