Guardare il video utilizzando Picture in picture

François Beaufort
François Beaufort

Picture in picture (PIP) consente agli utenti di guardare i video in una finestra mobile (sempre sopra le altre finestre), in modo da poter tenere d'occhio ciò che stanno guardando mentre interagiscono con altri siti o applicazioni.

Con l'API Picture in picture, puoi avviare e controllare Picture in picture per gli elementi video del tuo sito web. Provala con il nostro esempio ufficiale di Picture in picture.

Contesto

A settembre 2016, Safari ha aggiunto il supporto Picture in picture tramite un'API WebKit in macOS Sierra. Sei mesi dopo, Chrome ha riprodotto automaticamente il video Picture in picture sui dispositivi mobili con il rilascio di Android O mediante un'API Android nativa. Sei mesi dopo, abbiamo annunciato la nostra intenzione di creare e standardizzare un'API web, una funzionalità compatibile con Safari, che avrebbe consentito agli sviluppatori web di creare e controllare l'esperienza completa relativa a Picture in picture. Ed eccoci qui.

Scopri il codice

Attiva Picture in picture

Iniziamo semplicemente con un elemento video e un modo per consentire all'utente di interagire con l'elemento, ad esempio un elemento pulsante.

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

Richiedi Picture in picture solo in risposta a un gesto dell'utente e mai nella promessa restituita da videoElement.play(). Questo perché le promesse non ancora propagano i gesti degli utenti. Richiama requestPictureInPicture() in un gestore di clic su pipButtonElement, come mostrato di seguito. È tua responsabilità gestire cosa succede se un utente fa clic due volte.

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

  await videoElement.requestPictureInPicture();

  pipButtonElement.disabled = false;
});

Quando la promessa si risolve, Chrome riduce il video in una piccola finestra che l'utente può spostare e posizionare su altre finestre.

Hai finito. Ottimo! Puoi smettere di leggere e andare a fare la tua meritata vacanza. Purtroppo, non è sempre così. La promessa potrebbe rifiutare per uno dei seguenti motivi:

  • La funzione Picture in picture non è supportata dal sistema.
  • Il documento non è autorizzato a utilizzare Picture in picture a causa di norme sulle autorizzazioni restrittive.
  • I metadati del video non sono stati ancora caricati (videoElement.readyState === 0).
  • Il file video è solo audio.
  • Il nuovo attributo disablePictureInPicture è presente nell'elemento video.
  • La chiamata non è stata effettuata in un gestore di eventi Gesto dell'utente (ad esempio il clic su un pulsante). A partire da Chrome 74, questa opzione è applicabile solo se non è già presente un elemento in Picture in picture.

La sezione Supporto delle funzionalità di seguito mostra come attivare/disattivare un pulsante in base a queste limitazioni.

Aggiungiamo un blocco try...catch per acquisire questi potenziali errori e far sapere all'utente cosa sta succedendo.

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'elemento video si comporta allo stesso modo indipendentemente dal fatto che sia in Picture in picture o no: gli eventi vengono attivati e i metodi di chiamata funzionano. Riflette i cambiamenti di stato nella finestra Picture in picture (ad esempio riproduzione, pausa, ricerca e così via) ed è anche possibile cambiarlo in modo programmatico in JavaScript.

Esci da Picture in picture

Ora impostiamo il pulsante per entrare e uscire da Picture in picture. Dobbiamo innanzitutto verificare se l'oggetto di sola lettura document.pictureInPictureElement è il nostro elemento video. In caso contrario, ti invieremo una richiesta per inserire Picture in picture come indicato sopra. In caso contrario, ti chiediamo di uscire chiamando document.exitPictureInPicture(), il che significa che il video verrà visualizzato di nuovo nella scheda originale. Tieni presente che questo metodo restituisce anche una promessa.

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

Ascoltare gli eventi Picture in picture

Generalmente, i sistemi operativi limitano la modalità Picture in picture a una sola finestra, pertanto l'implementazione di Chrome segue questo schema. Ciò significa che gli utenti possono riprodurre un solo video Picture in picture alla volta. Gli utenti dovrebbero uscire dalla modalità Picture in picture anche senza che tu l'abbia richiesta.

I nuovi gestori di eventi enterpictureinpicture e leavepictureinpicture consentono di personalizzare l'esperienza per gli utenti. Può essere qualsiasi cosa, dalla navigazione di un catalogo di video alla visualizzazione di una chat in live streaming.

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

Personalizzare la finestra Picture in picture

Chrome 74 supporta i pulsanti Riproduci/Metti in pausa, Traccia precedente e Traccia successiva nella finestra Picture in picture che puoi controllare utilizzando l'API Media Session.

Controlli per la riproduzione di contenuti multimediali in una finestra Picture in picture
Figura 1. Controlli per la riproduzione di contenuti multimediali in una finestra Picture in picture

Per impostazione predefinita, un pulsante di riproduzione/pausa viene sempre visualizzato nella finestra Picture in picture a meno che il video non stia riproducendo oggetti MediaStream (ad es. getUserMedia(), getDisplayMedia(), canvas.captureStream()) o che il video non abbia una durata MediaSource impostata su +Infinity (ad es. feed live). Per assicurarti che un pulsante di riproduzione/pausa sia sempre visibile, imposta alcuni gestori di azioni Media Session per gli eventi multimediali "Riproduci" e "Metti in pausa" come indicato di seguito.

// 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.
});

La visualizzazione dei controlli delle finestre "Traccia precedente" e "Traccia successiva" è simile. Se imposti i gestori delle azioni della sessione multimediale, questi verranno visualizzati nella finestra Picture in picture e sarai in grado di gestire queste azioni.

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

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

Per vedere come funziona, prova l'esempio di sessione multimediale ufficiale.

Ottenere le dimensioni della finestra Picture in picture

Se desideri regolare la qualità del video quando il video entra ed esce dalla modalità Picture in picture, devi conoscere le dimensioni della finestra Picture in picture e ricevere una notifica se un utente ridimensiona manualmente la finestra.

L'esempio seguente mostra come ottenere la larghezza e l'altezza della finestra Picture in picture quando viene creata o ridimensionata.

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

Suggerisco di non collegare direttamente l'evento di ridimensionamento perché ogni piccola modifica apportata alla dimensione della finestra Picture in picture attiverà un evento separato che potrebbe causare problemi di prestazioni se esegui un'operazione costosa a ogni ridimensionamento. In altre parole, l'operazione di ridimensionamento attiva gli eventi più e più volte molto rapidamente. Per risolvere questo problema, ti consiglio di utilizzare tecniche comuni come la limitazione e il debouncing.

Supporto delle funzionalità

L'API Picture-in-Picture Web potrebbe non essere supportata, quindi devi rilevarla per fornire un miglioramento progressivo. Anche quando è supportata, potrebbe essere disattivata dall'utente o disabilitata da un criterio di autorizzazione. Fortunatamente, puoi utilizzare il nuovo valore booleano document.pictureInPictureEnabled per determinarlo.

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.');
}

Applicato a un elemento pulsante specifico per un video, ecco come puoi gestire la visibilità del pulsante 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;
}

Supporto di video MediaStream

I video che riproducono oggetti MediaStream (ad esempio getUserMedia(), getDisplayMedia() canvas.captureStream()) supportano anche la funzionalità Picture in picture in Chrome 71. Ciò significa che puoi mostrare una finestra Picture in picture che contiene lo stream video della webcam dell'utente, lo stream video visualizzato o persino un elemento canvas. Tieni presente che non è necessario che l'elemento video sia collegato al DOM per attivare Picture in picture, come mostrato di seguito.

Mostra la webcam dell'utente nella finestra 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();

Mostra visualizzazione nella finestra 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();

Mostra elemento canvas nella finestra 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();

Combinando canvas.captureStream() con l'API Media Session, puoi, ad esempio, creare una finestra della playlist audio in Chrome 74. Dai un'occhiata all'esempio ufficiale della playlist audio.

Playlist audio in una finestra Picture in picture
Figura 2. Playlist audio in una finestra Picture in picture

Esempi, demo e codelab

Dai un'occhiata al nostro esempio ufficiale di immagine per provare l'API web Picture in picture.

Seguiranno demo e codelab.

Passaggi successivi

Innanzitutto, visita la pagina di stato dell'implementazione per sapere quali parti dell'API sono attualmente implementate in Chrome e altri browser.

Ecco che cosa puoi aspettarti di vedere nel prossimo futuro:

Supporto del browser

L'API Picture-in-Picture Web è supportata in Chrome, Edge, Opera e Safari. Per maggiori dettagli, vedi MDN.

Risorse

Grazie mille a Mounir Lamouri e Jennifer Apacible per il loro lavoro su Picture in picture e per la collaborazione con questo articolo. Grazie di cuore a tutti coloro che sono coinvolti nell'impegno di standardizzazione.