Regarder une vidéo en mode Picture-in-picture

François Beaufort
François Beaufort

Le Picture-in-picture (PIP) permet aux utilisateurs de regarder des vidéos dans une fenêtre flottante (toujours au-dessus des autres fenêtres) afin qu'il puisse garder un œil sur ce qu'il tout en interagissant avec d'autres sites ou applications.

Avec l'API Web Picture-in-picture, vous pouvez démarrer et contrôler Picture-in-picture pour les éléments vidéo de votre site Web. Essayez sur notre échantillon Picture-in-picture officiel.

Contexte

Depuis septembre 2016, Safari est compatible avec la fonctionnalité Picture-in-picture via une API WebKit. dans macOS Sierra. Six mois plus tard, Chrome jouait automatiquement Vidéo Picture-in-picture sur mobile avec la sortie d'Android O à l'aide d'un API Android native. Six mois plus tard, nous avons annoncé notre intention de créer et standardiser une API Web, une fonctionnalité compatible avec Safari, qui permettrait d'accéder aux développeurs de créer et de contrôler l'expérience complète du Picture-in-picture. Et voilà !

Découvrir le code

Utiliser le mode Picture-in-picture

Commençons simplement par un élément vidéo et un moyen d'interagir avec l'utilisateur. avec lui, comme un élément de bouton.

<video id="videoElement" src="https://example.com/file.mp4"></video>
<button id="pipButtonElement"></button>

Ne demandez le mode Picture-in-picture qu'en réponse à un geste de l'utilisateur, et jamais promesse renvoyée par videoElement.play(). En effet, les promesses mais propager les gestes de l'utilisateur. Appelez plutôt requestPictureInPicture() dans un gestionnaire de clics sur pipButtonElement, comme illustré ci-dessous. Il est de votre responsabilité pour gérer ce qui se passe si un utilisateur clique deux fois.

pipButtonElement.addEventListener('click', async function () {
  pipButtonElement.disabled = true;

  await videoElement.requestPictureInPicture();

  pipButtonElement.disabled = false;
});

Lorsque la promesse est résolue, Chrome réduit la vidéo dans une petite fenêtre qui l'utilisateur peut se déplacer et se positionner par-dessus d'autres fenêtres.

Vous avez terminé. Bravo ! Vous pouvez arrêter de lire et aller prendre votre vacances. Malheureusement, ce n'est pas toujours le cas. La promesse peut être rejetée pour toute des raisons suivantes:

  • Le Picture-in-picture n'est pas pris en charge par le système.
  • L'utilisation du mode Picture-in-picture n'est pas autorisée sur le document en raison d'une règle d'autorisation.
  • Les métadonnées de la vidéo n'ont pas encore été chargées (videoElement.readyState === 0).
  • Le fichier vidéo est audio uniquement.
  • Le nouvel attribut disablePictureInPicture est présent dans l'élément vidéo.
  • L'appel n'a pas été effectué dans un gestionnaire d'événements de gestes de l'utilisateur (par exemple, un clic sur un bouton). À partir de Chrome 74, cela n'est applicable que s'il n'y a pas d'élément dans Picture-in-picture déjà.

La section Compatibilité des fonctionnalités ci-dessous indique comment activer/désactiver un bouton en fonction du ces restrictions.

Ajoutons un bloc try...catch pour capturer ces erreurs potentielles et laissons de savoir ce qui se passe.

pipButtonElement.addEventListener('click', async function () {
  pipButtonElement.disabled = true;

  try {
    await videoElement.requestPictureInPicture();
  } catch (error) {
    // TODO: Show error message to user.
  } finally {
    pipButtonElement.disabled = false;
  }
});

L'élément vidéo se comporte de la même manière, qu'il soit en mode Picture-in-picture non: les événements sont déclenchés et les méthodes d'appel fonctionnent. Elle reflète les changements d'état dans la fenêtre Picture-in-picture (pour lire, mettre en pause, rechercher, etc.) et de modifier l'état par programmation en JavaScript.

Quitter le mode Picture-in-picture

Maintenant, nous allons faire en sorte que notre bouton passe en mode Picture-in-picture. Mer vous devez d'abord vérifier si l'objet en lecture seule document.pictureInPictureElement est notre élément vidéo. Si ce n'est pas le cas, nous vous envoyons une demande d'inscription Picture-in-picture comme ci-dessus. Sinon, nous vous demandons de nous quitter en appelant document.exitPictureInPicture(), ce qui signifie que la vidéo sera de nouveau disponible dans dans l'onglet d'origine. Notez que cette méthode renvoie également une promesse.

    ...
    try {
      if (videoElement !== document.pictureInPictureElement) {
        await videoElement.requestPictureInPicture();
      } else {
        await document.exitPictureInPicture();
      }
    }
    ...

Écouter des événements Picture-in-picture

Les systèmes d'exploitation limitent généralement le Picture-in-picture à une seule fenêtre, L'implémentation de Chrome suit ce schéma. Cela signifie que les utilisateurs ne peuvent jouer une vidéo Picture-in-picture à la fois. Vous devez vous attendre à ce que les utilisateurs quittent Picture-in-picture même si vous ne l'avez pas demandé.

Les nouveaux gestionnaires d'événements enterpictureinpicture et leavepictureinpicture permettent d'adapter l'expérience aux utilisateurs. Il peut s'agir de n'importe quoi, de la consultation d'un ou à afficher un chat en direct.

videoElement.addEventListener('enterpictureinpicture', function (event) {
  // Video entered Picture-in-Picture.
});

videoElement.addEventListener('leavepictureinpicture', function (event) {
  // Video left Picture-in-Picture.
  // User may have played a Picture-in-Picture video from a different page.
});

Personnaliser la fenêtre Picture-in-picture

Chrome 74 prend en charge les boutons lecture/pause, titre précédent et morceau suivant dans Fenêtre Picture-in-picture que vous pouvez contrôler à l'aide de l'API Media Session.

Commandes de lecture de contenus multimédias dans une fenêtre Picture-in-picture
Image 1. Commandes de lecture de contenus multimédias dans une fenêtre Picture-in-picture

Par défaut, un bouton de lecture/pause est toujours affiché dans la sauf si la vidéo lit des objets MediaStream (par exemple, getUserMedia(), getDisplayMedia() ou canvas.captureStream()) ou si la vidéo dispose d'une MediaSource la durée doit être définie sur +Infinity (flux en direct, par exemple). Pour vous assurer qu’un bouton lecture/pause est toujours visible, définissez des gestionnaires d'actions de session multimédia à la fois pour "Lecture" et "Mets en pause" les événements média comme ci-dessous.

// Show a play/pause button in the Picture-in-Picture window
navigator.mediaSession.setActionHandler('play', function () {
  // User clicked "Play" button.
});
navigator.mediaSession.setActionHandler('pause', function () {
  // User clicked "Pause" button.
});

Affichage du titre précédent et "Titre suivant" les commandes de fenêtre sont similaires. Paramètre Les gestionnaires d'actions de sessions multimédias associés à ces éléments les afficheront dans le mode Picture-in-picture. et vous serez en mesure de gérer ces actions.

navigator.mediaSession.setActionHandler('previoustrack', function () {
  // User clicked "Previous Track" button.
});

navigator.mediaSession.setActionHandler('nexttrack', function () {
  // User clicked "Next Track" button.
});

Pour voir comment cela fonctionne, essayez l'exemple officiel de session multimédia.

Obtenir la taille de la fenêtre Picture-in-picture

Si vous souhaitez régler la qualité à l'entrée et à la sortie de la vidéo vous devez connaître la taille de la fenêtre Picture-in-picture est averti si un utilisateur redimensionne manuellement la fenêtre.

L'exemple ci-dessous montre comment obtenir la largeur et la hauteur Fenêtre Picture-in-picture lorsqu'elle est créée ou redimensionnée.

let pipWindow;

videoElement.addEventListener('enterpictureinpicture', function (event) {
  pipWindow = event.pictureInPictureWindow;
  console.log(`> Window size is ${pipWindow.width}x${pipWindow.height}`);
  pipWindow.addEventListener('resize', onPipWindowResize);
});

videoElement.addEventListener('leavepictureinpicture', function (event) {
  pipWindow.removeEventListener('resize', onPipWindowResize);
});

function onPipWindowResize(event) {
  console.log(
    `> Window size changed to ${pipWindow.width}x${pipWindow.height}`
  );
  // TODO: Change video quality based on Picture-in-Picture window size.
}

Je vous conseille de ne pas connecter directement l'événement de redimensionnement à chaque petite modification apportée. à la taille de la fenêtre Picture-in-picture déclenche un événement distinct qui peut entraîner de performances si vous effectuez une opération coûteuse à chaque redimensionnement. Dans En d'autres termes, l'opération de redimensionnement déclenchera les événements à l'infini rapidement. Je vous recommande d'utiliser des techniques courantes telles que la limitation par rebond pour résoudre ce problème.

Compatibilité des caractéristiques

L'API Web Picture-in-picture n'est peut-être pas compatible. Vous devez donc le détecter. pour apporter une amélioration progressive. Même s'il est pris en charge, il peut être désactivée par l'utilisateur ou désactivée par une règle d'autorisation. Heureusement, vous pouvez utiliser la nouvelle valeur booléenne document.pictureInPictureEnabled pour le déterminer.

if (!('pictureInPictureEnabled' in document)) {
  console.log('The Picture-in-Picture Web API is not available.');
} else if (!document.pictureInPictureEnabled) {
  console.log('The Picture-in-Picture Web API is disabled.');
}

Appliqué à un élément de bouton spécifique dans une vidéo, voici comment procéder : pour gérer la visibilité du bouton Picture-in-picture.

if ('pictureInPictureEnabled' in document) {
  // Set button ability depending on whether Picture-in-Picture can be used.
  setPipButton();
  videoElement.addEventListener('loadedmetadata', setPipButton);
  videoElement.addEventListener('emptied', setPipButton);
} else {
  // Hide button if Picture-in-Picture is not supported.
  pipButtonElement.hidden = true;
}

function setPipButton() {
  pipButtonElement.disabled =
    videoElement.readyState === 0 ||
    !document.pictureInPictureEnabled ||
    videoElement.disablePictureInPicture;
}

Compatibilité vidéo avec MediaStream

Une vidéo lit des objets MediaStream (par exemple, getUserMedia(), getDisplayMedia(), canvas.captureStream()) sont également compatibles avec le Picture-in-picture dans Chrome 71. Ce signifie que vous pouvez afficher une fenêtre Picture-in-picture qui contient la webcam de l'utilisateur un flux vidéo display, un flux vidéo display ou même un élément de canevas. Notez que l'élément vidéo n'a pas besoin d'être associé au DOM Picture-in-picture comme indiqué ci-dessous.

Afficher la webcam de l'utilisateur dans la fenêtre Picture-in-picture

const video = document.createElement('video');
video.muted = true;
video.srcObject = await navigator.mediaDevices.getUserMedia({video: true});
video.play();

// Later on, video.requestPictureInPicture();

Afficher l'affichage dans la fenêtre Picture-in-picture

const video = document.createElement('video');
video.muted = true;
video.srcObject = await navigator.mediaDevices.getDisplayMedia({video: true});
video.play();

// Later on, video.requestPictureInPicture();

Afficher l'élément canevas dans la fenêtre Picture-in-picture

const canvas = document.createElement('canvas');
// Draw something to canvas.
canvas.getContext('2d').fillRect(0, 0, canvas.width, canvas.height);

const video = document.createElement('video');
video.muted = true;
video.srcObject = canvas.captureStream();
video.play();

// Later on, video.requestPictureInPicture();

En combinant canvas.captureStream() avec l'API Media Session, vous pouvez : créer une fenêtre de playlist audio dans Chrome 74. Consultez la version officielle Exemple de playlist audio.

Playlist audio dans une fenêtre Picture-in-picture
Figure 2 : Playlist audio dans une fenêtre Picture-in-picture

Exemples, démonstrations et ateliers de programmation

Consultez notre exemple officiel Picture-in-picture pour essayer la fonctionnalité Picture-in-picture API Web.

Des démonstrations et des ateliers de programmation suivront.

Étapes suivantes

Tout d'abord, consultez la page d'état de l'implémentation pour savoir quelles parties sont actuellement implémentées dans Chrome et d'autres navigateurs.

Voici ce qui vous attend prochainement:

Prise en charge des navigateurs

L'API Web Picture-in-picture est compatible avec Chrome, Edge, Opera et Safari. Pour en savoir plus, consultez la page MDN.

Ressources

Un grand merci à Mounir Lamouri et Jennifer Apacible pour leur travail sur Picture-in-picture, puis consultez cet article. Et un grand merci à tous impliquées dans l'effort de normalisation.