Lire du contenu audio et vidéo avec MediaPlayer
Cet article vous montre comment lire des médias dans votre application Windows universelle à l’aide de 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 simplifiée à processus unique pour l’audio en arrière-plan, l’intégration automatique aux contrôles de transport multimédia système (SMTC), la possibilité de synchroniser plusieurs lecteurs multimédias, la possibilité d’afficher des images vidéo sur une surface Windows.UI.Composition et une interface facile pour créer et planifier des sauts multimédias dans votre contenu. Pour tirer parti de ces améliorations, la meilleure pratique recommandée pour la lecture de médias consiste à utiliser la classe MediaPlayer au lieu de MediaElement pour la lecture multimédia. Le contrôle XAML léger, MediaPlayerElement, a été introduit pour vous permettre de restituer du contenu multimédia dans une page XAML. La plupart des API de contrôle de lecture et d’état 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 fonctionnalité supplémentaire n’est ajoutée à cette classe.
Cet article vous guidera tout au long des fonctionnalités de MediaPlayer qu’une application de lecture multimédia classique utilisera. Notez que MediaPlayer utilise la classe MediaSource comme conteneur pour tous les éléments multimédias. Cette classe vous permet de charger et de lire des médias à partir de nombreuses sources différentes, notamment les fichiers locaux, les flux de mémoire et les sources réseau, tous utilisant la même interface. Il existe également des classes de niveau supérieur qui fonctionnent avec MediaSource, telles que MediaPlaybackItem et MediaPlaybackList, qui fournissent des fonctionnalités plus avancées telles que des playlists et la possibilité de gérer des 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 éléments multimédias, playlists et pistes.
Remarque
Les éditions Windows 10 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 le pack de fonctionnalités Media pour les éditions Windows 10 N et Windows 10 KN.
Lire un fichier multimédia avec MediaPlayer
La lecture multimédia de base avec MediaPlayer est très simple à implémenter. Tout d’abord, créez une instance de la classe MediaPlayer . Votre application peut avoir plusieurs instances MediaPlayer actives à la fois. 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 MediaSource est créé à partir d’un fichier dans le stockage local de l’application, puis un MediaPlaybackItem est créé à partir de la source, puis affecté à la propriété Source du lecteur.
Contrairement à MediaElement, MediaPlayer ne commence pas automatiquement la lecture par défaut. Vous pouvez démarrer la lecture en appelant Lecture, 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();
Une fois votre application effectuée à l’aide d’un MediaPlayer, vous devez appeler la méthode Close (projetée pour Supprimer en C#) pour nettoyer les ressources utilisées par le lecteur.
mediaPlayer.Dispose();
Utiliser MediaPlayerElement pour afficher la vidéo en XAML
Vous pouvez lire des médias dans un MediaPlayer sans l’afficher en XAML, mais de nombreuses applications de lecture multimédia souhaitent afficher le média dans une page XAML. Pour ce faire, utilisez le contrôle MediaPlayerElement léger. Comme MediaElement, MediaPlayerElement vous permet de spécifier si les contrôles de transport intégrés 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 MediaPlayerElement et l’élément crée automatiquement une instance MediaPlayer accessible à l’aide de la propriété MediaPlayer .
Remarque
La définition des propriétés MediaPlayerElement définit les propriétés correspondantes sur son MediaPlayer sous-jacent. Vous avez la possibilité d’utiliser directement MediaPlayer sous-jacent au lieu d’utiliser les propriétés MediaPlayerElement. N’oubliez pas que l’utilisation de MediaPlayer directement où une propriété MediaPlayerElement équivalente peut sinon être utilisée peut entraîner un comportement inattendu. Cela est dû au fait que MediaPlayerElement n’est pas conscient de tout ce qui se passe à son MediaPlayer sous-jacent. Par exemple, si vous définissez la source directement sur MediaPlayer, la propriété Source MediaPlayerElement ne reflète pas la modification. Pour cette raison, vous devez être cohérent dans l’utilisation des propriétés MediaPlayerElement ou directement à l’aide de MediaPlayer sous-jacent.
_mediaPlayerElement.Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/example_video.mkv"));
mediaPlayer = _mediaPlayerElement.MediaPlayer;
mediaPlayer.Play();
Remarque
Si vous désactivez MediaPlaybackCommandManager du MediaPlayer en définissant IsEnabled sur false, il interrompt le lien entre MediaPlayer les TransportControls fournis par MediaPlayerElement, de sorte que les contrôles de transport intégrés ne contrôlent plus automatiquement la lecture du lecteur. Au lieu de cela, vous devez implémenter vos propres contrôles pour contrôler MediaPlayer.
MediaPlayer est détaché de MediaPlayerElement lorsque MediaPlayerElement est détruit ou lorsqu’un nouveau MediaPlayer est défini à l’aide de SetMediaPlayer. En cas de détachement, MediaPlayerElement traite le MediaPlayer sous-jacent différemment selon qu’il a été créé par MediaPlayerElement ou défini à l’aide de SetMediaPlayer.
Si MediaPlayer a été créé par MediaPlayerElement, il ferme correctement MediaPlayer pour vous. Si MediaPlayer a été défini sur MediaPlayerElement à l’aide de SetMediaPlayer, vous êtes responsable de la fermeture correcte de MediaPlayer. L’échec de cette opération peut entraîner des erreurs de lecture irrécupérables dans MediaPlayer. L’extrait de code suivant montre comment détacher et fermer correctement le code.
// Get a reference to the current media source.
IMediaPlaybackSource _mediaPlayerElement = _mediaPlayerElement.Source;
// Pause playback if able.
if (mediaPlayer.PlaybackSession.CanPause)
{
mediaPlayer.Pause();
}
// Disconnect the MediaPlayer from its source. This can be done by setting
// the MediaPlayerElement Source property to null or by directly setting the
// source to null on the underlying MediaPlayer.
_mediaPlayerElement.Source = null;
// Disconnect the MediaPlayer from MediaPlayerElement.
_mediaPlayerElement.SetMediaPlayer(null);
// Dispose of the MediaPlayer or Source if they're no longer needed.
if (source is MediaSource mediaSource)
{
mediaSource.Dispose();
}
mediaPlayer.Dispose();
Tâches MediaPlayer courantes
Cette section vous montre comment utiliser certaines des fonctionnalités de MediaPlayer.
Définir la catégorie audio
Définissez la propriété AudioCategory d’un MediaPlayer sur l’une des valeurs de l’énumération MediaPlayerAudioCategory pour informer le système du type de média que vous jouez. Les jeux doivent classer leurs flux de musique en tant que GameMedia afin que la musique de jeu se mute automatiquement si une autre application joue de la musique en arrière-plan. Les applications musicales ou vidéo doivent catégoriser leurs flux comme Media ou Movie afin qu’elles prennent la priorité sur les flux GameMedia.
mediaPlayer.AudioCategory = MediaPlayerAudioCategory.Media;
Sortie vers un point de terminaison audio spécifique
Par défaut, la sortie audio d’un MediaPlayer est routée vers le point de terminaison audio par défaut pour le système, mais vous pouvez spécifier un point de terminaison audio spécifique que MediaPlayer doit utiliser pour la sortie. Dans l’exemple ci-dessous, MediaDevice.GetAudioRenderSelector retourne une chaîne qui identifie de manière unique la catégorie de rendu audio des appareils. Ensuite, la méthode DeviceInformation FindAllAsync est appelée pour obtenir la liste de tous les appareils disponibles du type sélectionné. Vous pouvez déterminer par programme l’appareil que vous souhaitez utiliser ou ajouter les appareils retournés à un 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 la zone de liste déroulante SelectionChanged pour les appareils, la propriété AudioDevice du MediaPlayer est définie sur l’appareil sélectionné, qui a été stocké dans la propriété Tag de l’objet 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, de nombreuses fonctions exposées par la classe MediaElement ont été déplacées vers la classe MediaPlaybackSession. Cela inclut des informations sur l’état de lecture du lecteur, telles que la position de lecture actuelle, que le lecteur soit suspendu ou en lecture, et la vitesse de lecture actuelle. MediaPlaybackSession fournit également plusieurs événements pour vous avertir lorsque l’état change, y compris la mise en mémoire tampon actuelle et l’état de téléchargement du contenu en cours de lecture et la taille naturelle et le ratio d’aspect du contenu vidéo en cours de lecture.
L’exemple suivant montre comment implémenter un gestionnaire de clics de bouton qui ignore 10 secondes de transfert dans le contenu. Tout d’abord, l’objet MediaPlaybackSession pour le lecteur est récupéré avec la propriété PlaybackSession. Ensuite, la propriété Position est définie sur la position de lecture actuelle plus de 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 pour basculer entre la vitesse de lecture normale et la vitesse 2X 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 quand le fichier multimédia en cours de lecture commence et met fin à la mise en mémoire tampon, 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 lorsqu’un fichier multimédia est ouvert pour la première fois 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 fournissant les problèmes techniques de contenu. À compter 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 vérifiez 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, il s’agit d’une mise en mémoire tampon initiale 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
}
Pincer et zoomer la vidéo
MediaPlayer vous permet de spécifier le rectangle source dans le contenu vidéo qui doit être affiché, ce qui vous permet de zoomer efficacement dans la vidéo. Le rectangle que vous spécifiez est relatif à un rectangle normalisé (0,0,1,1) où 0,0 est la main supérieure gauche du cadre et 1,1 spécifie la largeur et la hauteur complètes du cadre. Par exemple, pour définir le rectangle de zoom afin que le quadrant supérieur droit de la vidéo soit rendu, vous devez spécifier le rectangle (.5,0,.5,5). Il est important de vérifier vos valeurs pour vous assurer que votre rectangle source se trouve dans le rectangle normalisé (0,0,1,1). Toute tentative de définition d’une valeur en dehors de cette plage entraîne la levée d’une exception.
Pour implémenter le pincement et le zoom à l’aide de mouvements tactiles multiples, vous devez d’abord spécifier les mouvements que vous souhaitez prendre en charge. Dans cet exemple, les mouvements de mise à l’échelle et de traduction sont demandés. L’événement ManipulationDelta est déclenché lorsque l’un des mouvements abonnés se produit. L’événement DoubleTapped sera utilisé pour réinitialiser le zoom sur l’image complète.
_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 de zoom actuel.
Rect _sourceRect = new Rect(0, 0, 1, 1);
Le gestionnaire ManipulationDelta ajuste l’échelle ou la traduction du rectangle de zoom. Si la valeur d’échelle delta n’est pas 1, cela signifie que l’utilisateur a effectué un mouvement de pincement. Si la valeur est supérieure à 1, le rectangle source doit être réduit pour effectuer un zoom avant dans le contenu. Si la valeur est inférieure à 1, le rectangle source doit être plus grand 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 d’échelle est 1, le mouvement de traduction est géré. Le rectangle est simplement traduit par le nombre de pixels en mouvement divisé par la largeur et la hauteur du contrôle. Là encore, le rectangle résultant est vérifié pour s’assurer qu’il se trouve dans les limites (0,0,1,1).
Enfin, la NormalizedSourceRect de MediaPlaybackSession est définie sur le rectangle nouvellement ajusté, en spécifiant la zone dans l’image vidéo qui doit être rendue.
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 défini sur (0,0,1,1) pour rendre 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;
}
REMARQUE Cette section décrit l’entrée tactile. Le pavé tactile envoie des événements de pointeur et n’envoie pas d’événements de 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 réduire 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étériorée par le système s’il est lu à 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 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 stratégie pour laquelle la vidéo est constrictée. Si la valeur n’est pas None, cet exemple enregistre la raison de dégradation à des fins de télémétrie. Cet exemple montre également la définition du débit binaire de AdaptiveMediaSource en cours de lecture sur la bande passante la plus faible pour enregistrer l’utilisation des données, car la vidéo est limitée et ne s’affiche pas à haute résolution de toute façon. Pour plus d’informations sur l’utilisation de AdaptiveMediaSource, consultez Streaming adaptatif.
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);
}
}
Utiliser MediaPlayerSurface pour afficher la vidéo dans une surface Windows.UI.Composition
À compter de Windows 10, version 1607, vous pouvez utiliser MediaPlayer pour restituer la vidéo à une ICompositionSurface, ce qui permet au lecteur d’interagir avec les API de l’espace de noms Windows.UI.Composition. L’infrastructure de composition vous permet d’utiliser des graphiques dans la couche visuelle entre XAML et les API graphiques DirectX de bas niveau. Cela permet des scénarios tels que le rendu de la vidéo dans n’importe quel contrôle XAML. Pour plus d’informations sur l’utilisation des API de composition, consultez Couche visuelle.
L’exemple suivant montre comment afficher le contenu du lecteur vidéo sur un contrôle Canvas. Les appels spécifiques au lecteur multimédia dans cet exemple sont SetSurfaceSize et GetSurface. SetSurfaceSize indique au système la taille de la mémoire tampon qui doit être allouée pour le rendu du contenu. GetSurface prend un compositor en tant qu’argument et récupère une instance de la classe MediaPlayerSurface. Cette classe permet d’accéder à MediaPlayer et compositor utilisé pour créer l’aire et expose la surface elle-même par le biais de la propriété CompositionSurface.
Le reste du code de cet exemple crée un SpriteVisual sur lequel la vidéo est affichée et définit la taille sur la taille de l’élément de canevas qui affiche le visuel. Ensuite, un objet CompositionBrush est créé à partir de MediaPlayerSurface et affecté à la propriété Brush du visuel. Ensuite, un ContainerVisual est créé et spriteVisual est inséré en haut de son arborescence visuelle. Enfin, SetElementChildVisual est appelé pour affecter le visuel conteneur au canevas.
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 pour synchroniser du contenu entre plusieurs lecteurs.
Comme indiqué précédemment dans cet article, votre application peut avoir plusieurs objets MediaPlayer actifs à la fois. Par défaut, chaque MediaPlayer que vous créez fonctionne indépendamment. Pour certains scénarios, tels que la synchronisation d’une piste de commentaire vers une vidéo, vous pouvez synchroniser l’état du lecteur, la position de lecture et la vitesse de lecture de plusieurs joueurs. À compter 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 montre comment utiliser un MediaTimelineController pour contrôler deux instances de MediaPlayer. Tout d’abord, chaque instance du MediaPlayer est instanciée et la source est définie sur un fichier multimédia. Ensuite, un nouveau MediaTimelineController est créé. Pour chaque MediaPlayer, MediaPlaybackCommandManager associé à chaque lecteur est désactivé en définissant la propriété IsEnabled sur false. Ensuite, la propriété TimelineController est définie sur l’objet du contrôleur de 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 Le MediaPlaybackCommandManager fournit une intégration automatique entre MediaPlayer et system Media Transport Controls (SMTC), mais cette intégration automatique ne peut pas être utilisée avec des lecteurs multimédias contrôlés avec un MediaTimelineController. Par conséquent, vous devez désactiver le gestionnaire de commandes du lecteur multimédia avant de définir le contrôleur de chronologie du lecteur. L’échec de cette opération entraîne la levée d’une exception avec le message suivant : « L’attachement du contrôleur de chronologie multimédia est bloqué en raison de l’état actuel de l’objet ». Pour plus d’informations sur l’intégration du lecteur multimédia à SMTC, consultez Intégrer aux contrôles de transport multimédia système. Si vous utilisez un MediaTimelineController , vous pouvez toujours contrôler manuellement le SMTC. Pour plus d’informations, consultez Contrôle manuel des contrôles de transport du support système.
Une fois que vous avez attaché un 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 Démarrer 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 la suspension et la reprise de tous les lecteurs multimédias attaché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 transférer rapidement tous les 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 montre comment utiliser un contrôle Slider pour afficher la position de lecture actuelle du contrôleur de chronologie par rapport à la durée du contenu de l’un des lecteurs multimédias connectés. Tout d’abord, un nouveau MediaSource est créé et un gestionnaire pour OpenOperationCompleted de la source multimédia est inscrit.
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é comme opportunité de découvrir la durée du contenu source multimédia. Une fois la durée 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 à l’intérieur d’un appel à RunAsync pour vous assurer qu’elle est exécutée 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 inscrit. Ceci est appelé périodiquement par le système, environ 4 fois par seconde.
_mediaTimelineController.PositionChanged += _mediaTimelineController_PositionChanged;
Dans le gestionnaire de PositionChanged, la valeur du curseur est mise à jour pour refléter 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, vous souhaiterez peut-être que la position de lecture d’un ou plusieurs lecteurs multimédias associés à un contrôleur de chronologie soit décalée par rapport aux autres joueurs. Pour ce faire, définissez la propriété TimelineControllerPositionOffset de l’objet MediaPlayer que vous souhaitez décaler. L’exemple suivant utilise les durées du contenu de deux lecteurs multimédias pour définir les valeurs minimales et maximales de deux contrôles curseur sur plus et 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 pour chaque curseur, TimelineControllerPositionOffset pour chaque lecteur est défini 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 est mappée à une position de lecture négative, le clip reste suspendu jusqu’à ce que le décalage atteigne zéro, puis la lecture commence. De même, si la valeur de décalage est mappée à une position de lecture supérieure à la durée de l’élément multimédia, l’image finale s’affiche, tout comme lorsqu’un seul lecteur multimédia atteint la fin de son contenu.
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 régulière et plate dans laquelle MediaPlayer affiche 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 qui spécifie que la vidéo utilise une projection équirectangulaire, MediaPlayer peut restituer la vidéo à l’aide d’un champ de vue et d’une orientation de vue spécifiés. Cela permet des scénarios tels que la lecture vidéo de réalité virtuelle avec un affichage monté sur la tête ou simplement permettre à l’utilisateur de parcourir le contenu vidéo sphérique à l’aide de la souris ou de l’entrée du clavier.
Pour lire la vidéo sphérique, suivez les étapes de lecture du contenu vidéo décrit précédemment dans cet article. L’une des étapes supplémentaires 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ériques.
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, vérifiez d’abord 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 les propriétés telles que l’orientation de la vue et le champ de vue que le lecteur multimédia utilisera pour projeter le contenu vidéo. Dans cet exemple, le champ d’affichage 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 qu’il est dans un format autre que équirectangulaire, vous pouvez implémenter votre propre algorithme de projection à l’aide du mode serveur frame 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 la vue 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 abordées en détail dans l’article, les éléments multimédias, les playlists et les 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 pour toutes les 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 MediaPlayerElement associé. Au lieu de cela, votre application copie le cadre actuel du MediaPlayer vers un objet qui implémente IDirect3DSurface. Ce scénario principal permet d’utiliser des nuanceurs de pixels pour traiter les images vidéo fournies par MediaPlayer. Votre application est chargée d’afficher chaque image après traitement, par exemple en affichant le cadre dans un contrôle d’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 à Lire.
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 flou simple à chaque image d’une vidéo, puis affiche les images traitées dans un contrôle d’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 CopyFrameToStereoscopiqueVideoSurfaces pour copier du contenu 3D dans deux surfaces, pour 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 d’un contrôle Image. Par conséquent, un nouvel objet est créé et défini comme source pour l’image dans laquelle le contenu sera affiché. Ensuite, un CanvasDrawingSession est créé. Il est utilisé par Win2D pour afficher l’effet flou.
Une fois que tous les objets nécessaires ont été instanciés, CopyFrameToVideoSurface est appelé, qui copie le cadre actuel du MediaPlayer dans canvasBitmap. Ensuite, un Win2D GaussianBlurEffect est créé, avec l’ensemble CanvasBitmap comme source de l’opération. Enfin, CanvasDrawingSession.DrawImage est appelé pour dessiner l’image source, avec l’effet flou appliqué, dans le CanvasImageSource associé au contrôle Image , ce qui l’a provoqué 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 dépôt GitHub Win2D. Pour tester l’exemple de code ci-dessus, vous devez ajouter le package NuGet Win2D à votre projet avec les instructions suivantes.
Pour ajouter le package NuGet Win2D à votre projet d’effet
- Dans l’Explorateur de solutions, cliquez avec le bouton droit sur le projet, puis sélectionnez Gérer les packages NuGet.
- En haut de la fenêtre, sélectionnez l’onglet Parcourir .
- Dans la zone de recherche, entrez Win2D.
- Sélectionnez Win2D.uwp, puis sélectionnez Installer dans le volet droit.
- La boîte de dialogue Révision des modifications affiche le package à installer. Cliquez sur OK.
- 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 diminue ou désactive le niveau audio d’un MediaPlayer en cours de lecture. Par exemple, le système peut diminuer, 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 votre application n’a pas déclaré la fonctionnalité backgroundMediaPlayback dans le manifeste de l’application. La classe AudioStateMonitor vous permet de vous enregistrer 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 pour être averti lorsque le niveau audio de ce MediaPlayer est modifié par le système.
mediaPlayer.AudioStateMonitor.SoundLevelChanged += AudioStateMonitor_SoundLevelChanged;
Lors de la gestion de l’événement SoundLevelChanged , vous pouvez effectuer différentes actions en fonction du type de contenu en cours de lecture. Si vous jouez actuellement de la musique, vous pouvez laisser la musique continuer à jouer pendant que le volume est canardé. Si vous jouez un podcast, toutefois, vous souhaiterez probablement suspendre la lecture pendant que l’audio est canardé 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 à suivre lorsque nous suspendons la lecture par programmation lorsque le niveau audio change.
bool isPodcast;
bool isPausedDueToAudioStateMonitor;
Dans le gestionnaire d’événements SoundLevelChanged, vérifiez la propriété SoundLevel de l’expéditeur AudioStateMonitor pour déterminer le nouveau niveau sonore. Cet exemple montre comment vérifier si le nouveau niveau sonore est plein volume, ce qui signifie que le système a cessé de désactiver ou de canarder le volume, ou si le niveau sonore a été réduit, mais qu’il joue du contenu non podcast. Si l’un de ces éléments est vrai et que le contenu a été précédemment suspendu par programmation, la lecture est reprise. 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 programme.
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 qu’il souhaite suspendre ou continuer la lecture, même si l’audio est canardé par le système. Cet exemple montre les gestionnaires d’événements pour une lecture et un bouton de pause. Dans le gestionnaire de clics du bouton de pause, si la lecture avait déjà été suspendue 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 décochons 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();
}
Rubriques connexes
- Lecture de contenu multimédia
- Éléments multimédias, playlists et pistes
- Intégrer aux contrôles de transport de média système
- Créer, planifier et gérer des sauts de média
- Lire du contenu multimédia en arrière-plan